blob: e84315a9c2a9918edfd3640597ed61eca6d7899c [file] [log] [blame]
/** @file
Code for processor configuration.
Copyright (c) 2013 Intel Corporation.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Module Name: ProcessorConfig.c
**/
#include "MpService.h"
#include "Cpu.h"
#include "MpApic.h"
MP_SYSTEM_DATA mMPSystemData;
CPU_CONFIG_CONTEXT_BUFFER mCpuConfigConextBuffer;
EFI_PHYSICAL_ADDRESS mStartupVector;
UINT8 mPlatformType;
ACPI_CPU_DATA *mAcpiCpuData;
EFI_HANDLE mHandle = NULL;
MTRR_SETTINGS *mMtrrSettings;
EFI_EVENT mSmmConfigurationNotificationEvent;
EFI_HANDLE mImageHandle;
EFI_TIMER_ARCH_PROTOCOL *mTimer;
UINTN mLocalApicTimerDivisor;
UINT32 mLocalApicTimerInitialCount;
/**
Prepares memory region for processor configuration.
This function prepares memory region for processor configuration.
**/
VOID
PrepareMemoryForConfiguration (
VOID
)
{
UINTN NumberOfProcessors;
UINTN Index;
MONITOR_MWAIT_DATA *MonitorData;
//
// Initialize Spin Locks for system
//
InitializeSpinLock (&mMPSystemData.APSerializeLock);
for (Index = 0; Index < PcdGet32(PcdCpuMaxLogicalProcessorNumber); Index++) {
InitializeSpinLock (&mMPSystemData.CpuData[Index].CpuDataLock);
}
//
// Claim memory for AP stack.
//
mExchangeInfo->StackStart = AllocateAcpiNvsMemoryBelow4G (PcdGet32(PcdCpuMaxLogicalProcessorNumber) * PcdGet32 (PcdCpuApStackSize));
mExchangeInfo->StackSize = PcdGet32 (PcdCpuApStackSize);
//
// Initialize the Monitor Data structure in APs' stack
//
for (Index = 0; Index < PcdGet32(PcdCpuMaxLogicalProcessorNumber); Index++) {
MonitorData = GetMonitorDataAddress (Index);
MonitorData->ApLoopMode = ApInHltLoop;
}
//
// Initialize data for CPU configuration context buffer
//
NumberOfProcessors = mCpuConfigConextBuffer.NumberOfProcessors;
mCpuConfigConextBuffer.CollectedDataBuffer = AllocateZeroPool (sizeof (CPU_COLLECTED_DATA) * NumberOfProcessors);
mCpuConfigConextBuffer.FeatureLinkListEntry = AllocateZeroPool (sizeof (LIST_ENTRY) * NumberOfProcessors);
//
// Initialize Processor Feature List for all logical processors.
//
for (Index = 0; Index < NumberOfProcessors; Index++) {
InitializeListHead (&mCpuConfigConextBuffer.FeatureLinkListEntry[Index]);
}
mCpuConfigConextBuffer.RegisterTable = AllocateAcpiNvsMemoryBelow4G (
(sizeof (CPU_REGISTER_TABLE) + sizeof (UINTN)) * NumberOfProcessors
);
mCpuConfigConextBuffer.PreSmmInitRegisterTable = AllocateAcpiNvsMemoryBelow4G (
(sizeof (CPU_REGISTER_TABLE) + sizeof (UINTN)) * NumberOfProcessors
);
mCpuConfigConextBuffer.SettingSequence = (UINTN *) (((UINTN) mCpuConfigConextBuffer.RegisterTable) + (sizeof (CPU_REGISTER_TABLE) * NumberOfProcessors));
for (Index = 0; Index < NumberOfProcessors; Index++) {
mCpuConfigConextBuffer.SettingSequence[Index] = Index;
}
//
// Set the value for PcdCpuConfigContextBuffer.
//
mCpuConfigLibConfigConextBuffer = &mCpuConfigConextBuffer;
PcdSet64 (PcdCpuConfigContextBuffer, (UINT64) (UINTN) mCpuConfigLibConfigConextBuffer);
//
// Read the platform type from PCD
//
mPlatformType = PcdGet8 (PcdPlatformType);
}
/**
Event notification that is fired every time a gEfiSmmConfigurationProtocol installs.
This function configures all logical processors with three-phase architecture.
@param Event The Event that is being processed, not used.
@param Context Event Context, not used.
**/
VOID
EFIAPI
SmmConfigurationEventNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
EFI_STATUS Status;
VOID *Registration;
EFI_SMM_CONFIGURATION_PROTOCOL *SmmConfiguration;
//
// Make sure this notification is for this handler
//
Status = gBS->LocateProtocol (&gEfiSmmConfigurationProtocolGuid, NULL, (VOID **)&SmmConfiguration);
if (EFI_ERROR (Status)) {
return;
}
//
// Wakeup APs. Collect data of all processors. BSP polls to
// wait for APs' completion.
//
DataCollectionPhase ();
//
// With collected data, BSP analyzes processors'configuration
// according to user's policy.
//
AnalysisPhase ();
//
// Wakeup APs. Program registers of all processors, according to
// the result of Analysis phase. BSP polls to wait for AP's completion.
//
SettingPhase ();
//
// Select least-feature procesosr as BSP
//
if (FeaturePcdGet (PcdCpuSelectLfpAsBspFlag)) {
SelectLfpAsBsp ();
}
//
// Add SMBIOS Processor Type and Cache Type tables for the CPU.
//
AddCpuSmbiosTables ();
//
// Save CPU S3 data
//
SaveCpuS3Data (mImageHandle);
Status = gBS->SetTimer (
mMPSystemData.CheckAPsEvent,
TimerPeriodic,
10000 * MICROSECOND
);
ASSERT_EFI_ERROR (Status);
//
// Setup notification on Legacy BIOS Protocol to reallocate AP wakeup
//
EfiCreateProtocolNotifyEvent (
&gEfiLegacyBiosProtocolGuid,
TPL_CALLBACK,
ReAllocateMemoryForAP,
NULL,
&Registration
);
}
/**
Early MP Initialization.
This function does early MP initialization, including MTRR sync and first time microcode load.
@param ProcessorNumber Handle number of specified logical processor.
**/
VOID
EarlyMpInit (
IN UINTN ProcessorNumber
)
{
MtrrSetAllMtrrs (mMtrrSettings);
CollectBasicProcessorData (ProcessorNumber);
}
/**
First phase MP initialization before SMM initialization.
@retval EFI_SUCCESS First phase MP initialization was done successfully.
@retval EFI_UNSUPPORTED There is legacy APIC ID conflict and can't be rsolved in xAPIC mode.
**/
EFI_STATUS
ProcessorConfiguration (
VOID
)
{
EFI_STATUS Status;
//
// Wakeup APs for the first time, BSP stalls for arbitrary
// time for APs' completion. BSP then collects the number
// and BIST information of APs.
//
WakeupAPAndCollectBist ();
//
// Sort APIC ID of all processors in asending order. Processor number
// is assigned in this order to ease MP debug. SMBIOS logic also depends on it.
//
SortApicId ();
//
// Prepare data in memory for processor configuration
//
PrepareMemoryForConfiguration ();
//
// Early MP initialization
//
mMtrrSettings = (MTRR_SETTINGS *)(UINTN)PcdGet64 (PcdCpuMtrrTableAddress);
MtrrGetAllMtrrs (mMtrrSettings);
DispatchAPAndWait (
TRUE,
0,
EarlyMpInit
);
EarlyMpInit (mCpuConfigConextBuffer.BspNumber);
DEBUG_CODE (
//
// Verify that all processors have same APIC ID topology. New APIC IDs
// were constructed based on this assumption.
//
UINTN Index;
UINT8 PackageIdBitOffset;
PackageIdBitOffset = mCpuConfigConextBuffer.CollectedDataBuffer[0].PackageIdBitOffset;
for (Index = 1; Index < mCpuConfigConextBuffer.NumberOfProcessors; Index++) {
if (PackageIdBitOffset != mCpuConfigConextBuffer.CollectedDataBuffer[Index].PackageIdBitOffset) {
ASSERT (FALSE);
}
}
);
//
// Check if there is legacy APIC ID conflict among all processors.
//
Status = CheckApicId ();
if (EFI_ERROR (Status)) {
return Status;
}
//
// Assign Package BSP for package scope programming later.
//
AssignPackageBsp ();
//
// Produce pre-SMM-init register table.
//
ProducePreSmmInitRegisterTable ();
//
// Early MP initialization step 2
//
DispatchAPAndWait (
TRUE,
0,
SetPreSmmInitProcessorRegister
);
SetPreSmmInitProcessorRegister (mCpuConfigConextBuffer.BspNumber);
//
// Locate Timer Arch Protocol
//
Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &mTimer);
ASSERT_EFI_ERROR (Status);
//
// Install MP Services Protocol
//
InstallMpServicesProtocol ();
return EFI_SUCCESS;
}
/**
Installs MP Services Protocol and register related timer event.
This function installs MP Services Protocol and register related timer event.
**/
VOID
InstallMpServicesProtocol (
VOID
)
{
EFI_STATUS Status;
//
// Create timer event to check AP state for non-blocking execution.
//
Status = gBS->CreateEvent (
EVT_TIMER | EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
CheckAPsStatus,
NULL,
&mMPSystemData.CheckAPsEvent
);
ASSERT_EFI_ERROR (Status);
//
// Now install the MP services protocol.
//
Status = gBS->InstallProtocolInterface (
&mHandle,
&gEfiMpServiceProtocolGuid,
EFI_NATIVE_INTERFACE,
&mMpService
);
ASSERT_EFI_ERROR (Status);
}
/**
Callback function for idle events.
@param Event Event whose notification function is being invoked.
@param Context The pointer to the notification function's context,
which is implementation-dependent.
**/
VOID
EFIAPI
IdleLoopEventCallback (
IN EFI_EVENT Event,
IN VOID *Context
)
{
CpuSleep ();
}
/**
Entrypoint of CPU MP DXE module.
This function is the entrypoint of CPU MP DXE module.
It initializes Multi-processor configuration and installs MP Services Protocol.
@param ImageHandle The firmware allocated handle for the EFI image.
@param SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The entrypoint always returns EFI_SUCCESS.
**/
EFI_STATUS
EFIAPI
MultiProcessorInitialize (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
VOID *Registration;
EFI_EVENT IdleLoopEvent;
EFI_EVENT ExitBootServiceEvent;
EFI_EVENT LegacyToBootEvent;
mImageHandle = ImageHandle;
//
// Configure processors with three-phase architecture
//
Status = ProcessorConfiguration ();
if (EFI_ERROR (Status)) {
return Status;
}
//
// Install notification callback on SMM Configuration Protocol
//
mSmmConfigurationNotificationEvent = EfiCreateProtocolNotifyEvent (
&gEfiSmmConfigurationProtocolGuid,
TPL_CALLBACK,
SmmConfigurationEventNotify,
NULL,
&Registration
);
//
// Create EXIT_BOOT_SERIVES Event to set AP to suitable status
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
ChangeApLoopModeCallBack,
NULL,
&gEfiEventExitBootServicesGuid,
&ExitBootServiceEvent
);
ASSERT_EFI_ERROR (Status);
//
// Create an event to be signalled when Legacy Boot occurs to set AP to suitable status
//
Status = EfiCreateEventLegacyBootEx(
TPL_NOTIFY,
ChangeApLoopModeCallBack,
NULL,
&LegacyToBootEvent
);
ASSERT_EFI_ERROR (Status);
//
// Setup a callback for idle events
//
Status = gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_NOTIFY,
IdleLoopEventCallback,
NULL,
&gIdleLoopEventGuid,
&IdleLoopEvent
);
ASSERT_EFI_ERROR (Status);
return EFI_SUCCESS;
}
/**
Wakes up APs for the first time to count their number and collect BIST data.
This function wakes up APs for the first time to count their number and collect BIST data.
**/
VOID
WakeupAPAndCollectBist (
VOID
)
{
//
// Save BSP's Local APIC Timer setting
//
GetApicTimerState (&mLocalApicTimerDivisor, NULL, NULL);
mLocalApicTimerInitialCount = GetApicTimerInitCount ();
//
// Prepare code and data for APs' startup vector
//
PrepareAPStartupVector ();
mCpuConfigConextBuffer.NumberOfProcessors = 1;
mCpuConfigConextBuffer.BspNumber = 0;
//
// Item 0 of BistBuffer is for BSP.
//
mExchangeInfo->BistBuffer[0].ApicId = GetInitialApicId ();
SendInitSipiSipiIpis (
TRUE,
0,
NULL
);
//
// Wait for task to complete and then exit.
//
MicroSecondDelay (PcdGet32 (PcdCpuApInitTimeOutInMicroSeconds));
mExchangeInfo->InitFlag = 0;
}
/**
Prepare ACPI NVS memory below 4G memory for use of S3 resume.
This function allocates ACPI NVS memory below 4G memory for use of S3 resume,
and saves data into the memory region.
@param Context The Context save the info.
**/
VOID
SaveCpuS3Data (
VOID *Context
)
{
MP_CPU_SAVED_DATA *MpCpuSavedData;
//
// Allocate ACPI NVS memory below 4G memory for use of S3 resume.
//
MpCpuSavedData = AllocateAcpiNvsMemoryBelow4G (sizeof (MP_CPU_SAVED_DATA));
//
// Set the value for CPU data
//
mAcpiCpuData = &(MpCpuSavedData->AcpiCpuData);
mAcpiCpuData->GdtrProfile = (EFI_PHYSICAL_ADDRESS) (UINTN) &(MpCpuSavedData->GdtrProfile);
mAcpiCpuData->IdtrProfile = (EFI_PHYSICAL_ADDRESS) (UINTN) &(MpCpuSavedData->IdtrProfile);
mAcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS) (UINTN) mExchangeInfo->StackStart;
mAcpiCpuData->StackSize = PcdGet32 (PcdCpuApStackSize);
mAcpiCpuData->MtrrTable = (EFI_PHYSICAL_ADDRESS) (UINTN) PcdGet64 (PcdCpuMtrrTableAddress);
mAcpiCpuData->RegisterTable = (EFI_PHYSICAL_ADDRESS) (UINTN) mCpuConfigConextBuffer.RegisterTable;
mAcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS) (UINTN) mCpuConfigConextBuffer.PreSmmInitRegisterTable;
mAcpiCpuData->ApMachineCheckHandlerBase = mApMachineCheckHandlerBase;
mAcpiCpuData->ApMachineCheckHandlerSize = mApMachineCheckHandlerSize;
//
// Check user's policy for HT enable.
//
mAcpiCpuData->APState = FALSE;
if ((PcdGet32 (PcdCpuProcessorFeatureUserConfiguration) & PCD_CPU_HT_BIT) != 0) {
mAcpiCpuData->APState = TRUE;
}
//
// Copy GDTR and IDTR profiles
//
CopyMem ((VOID *) (UINTN) mAcpiCpuData->GdtrProfile, (VOID *) (UINTN) &mExchangeInfo->GdtrProfile, sizeof (IA32_DESCRIPTOR));
CopyMem ((VOID *) (UINTN) mAcpiCpuData->IdtrProfile, (VOID *) (UINTN) &mExchangeInfo->IdtrProfile, sizeof (IA32_DESCRIPTOR));
mAcpiCpuData->NumberOfCpus = (UINT32) mCpuConfigConextBuffer.NumberOfProcessors;
//
// Set the base address of CPU S3 data to PcdCpuS3DataAddress
//
PcdSet64 (PcdCpuS3DataAddress, (UINT64)(UINTN)mAcpiCpuData);
}