/** @file\r
CPU DXE Module to produce CPU MP Protocol.\r
\r
- Copyright (c) 2008 - 2017, Intel Corporation. All rights reserved.<BR>\r
- This program and the accompanying materials\r
- are licensed and made available under the terms and conditions of the BSD License\r
- which accompanies this distribution. The full text of the license may be found at\r
- http://opensource.org/licenses/bsd-license.php\r
-\r
- THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
- WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+ Copyright (c) 2008 - 2022, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
#include "CpuDxe.h"\r
#include "CpuMp.h"\r
\r
-EFI_HANDLE mMpServiceHandle = NULL;\r
-UINTN mNumberOfProcessors = 1;\r
+EFI_HANDLE mMpServiceHandle = NULL;\r
+UINTN mNumberOfProcessors = 1;\r
\r
EFI_MP_SERVICES_PROTOCOL mMpServicesTemplate = {\r
GetNumberOfProcessors,\r
and releases the BSP to continue with other tasks.\r
-# The caller can use the CheckEvent() and WaitForEvent() services to check\r
the state of the WaitEvent created in step 1.\r
- -# When the APs complete their task or TimeoutInMicroSecondss expires, the MP\r
+ -# When the APs complete their task or TimeoutInMicroSeconds expires, the MP\r
Service signals WaitEvent by calling the EFI SignalEvent() function. If\r
FailedCpuList is not NULL, its content is available when WaitEvent is\r
signaled. If all APs returned from Procedure prior to the timeout, then\r
This function is used to dispatch one enabled AP to the function specified by\r
Procedure passing in the argument specified by ProcedureArgument. If WaitEvent\r
is NULL, execution is in blocking mode. The BSP waits until the AP finishes or\r
- TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.\r
+ TimeoutInMicroSeconds expires. Otherwise, execution is in non-blocking mode.\r
BSP proceeds to the next task without waiting for the AP. If a non-blocking mode\r
is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled,\r
then EFI_UNSUPPORTED must be returned.\r
OUT UINTN *ProcessorNumber\r
)\r
{\r
- return MpInitLibWhoAmI (ProcessorNumber);;\r
+ return MpInitLibWhoAmI (ProcessorNumber);\r
}\r
\r
/**\r
// Sec Platform Information2 PPI includes BSP/APs' BIST information\r
//\r
SecPlatformInformation2 = GET_GUID_HOB_DATA (GuidHob);\r
- NumberOfData = SecPlatformInformation2->NumberOfCpus;\r
- CpuInstance = SecPlatformInformation2->CpuInstance;\r
+ NumberOfData = SecPlatformInformation2->NumberOfCpus;\r
+ CpuInstance = SecPlatformInformation2->CpuInstance;\r
} else {\r
//\r
// Otherwise, get gEfiSecPlatformInformationPpiGuid Guided HOB\r
GuidHob = GetFirstGuidHob (&gEfiSecPlatformInformationPpiGuid);\r
if (GuidHob != NULL) {\r
SecPlatformInformation = GET_GUID_HOB_DATA (GuidHob);\r
- NumberOfData = 1;\r
+ NumberOfData = 1;\r
//\r
// SEC Platform Information only includes BSP's BIST information\r
// does not have BSP's APIC ID\r
//\r
- BspCpuInstance.CpuLocation = GetApicId ();\r
- BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32 = SecPlatformInformation->IA32HealthFlags.Uint32;\r
- CpuInstance = &BspCpuInstance;\r
+ BspCpuInstance.CpuLocation = GetApicId ();\r
+ BspCpuInstance.InfoRecord.IA32HealthFlags.Uint32 = SecPlatformInformation->IA32HealthFlags.Uint32;\r
+ CpuInstance = &BspCpuInstance;\r
} else {\r
DEBUG ((DEBUG_INFO, "Does not find any HOB stored CPU BIST information!\n"));\r
//\r
BistData = CpuInstance[CpuInstanceNumber].InfoRecord.IA32HealthFlags;\r
}\r
}\r
+\r
if (BistData.Uint32 != 0) {\r
//\r
// Report Status Code that self test is failed\r
}\r
}\r
\r
-/**\r
- Get GDT register value.\r
-\r
- This function is mainly for AP purpose because AP may have different GDT\r
- table than BSP.\r
-\r
- @param[in,out] Buffer The pointer to private data buffer.\r
-\r
-**/\r
-VOID\r
-EFIAPI\r
-GetGdtr (\r
- IN OUT VOID *Buffer\r
- )\r
-{\r
- AsmReadGdtr ((IA32_DESCRIPTOR *)Buffer);\r
-}\r
+//\r
+// Structure for InitializeSeparateExceptionStacks\r
+//\r
+typedef struct {\r
+ VOID *Buffer;\r
+ UINTN BufferSize;\r
+ EFI_STATUS Status;\r
+} EXCEPTION_STACK_SWITCH_CONTEXT;\r
\r
/**\r
Initializes CPU exceptions handlers for the sake of stack switch requirement.\r
\r
- This function is a wrapper of InitializeCpuExceptionHandlersEx. It's mainly\r
+ This function is a wrapper of InitializeSeparateExceptionStacks. It's mainly\r
for the sake of AP's init because of EFI_AP_PROCEDURE API requirement.\r
\r
@param[in,out] Buffer The pointer to private data buffer.\r
VOID\r
EFIAPI\r
InitializeExceptionStackSwitchHandlers (\r
- IN OUT VOID *Buffer\r
+ IN OUT VOID *Buffer\r
)\r
{\r
- CPU_EXCEPTION_INIT_DATA *EssData;\r
- IA32_DESCRIPTOR Idtr;\r
- EFI_STATUS Status;\r
+ EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData;\r
+ UINTN Index;\r
+\r
+ MpInitLibWhoAmI (&Index);\r
+ SwitchStackData = (EXCEPTION_STACK_SWITCH_CONTEXT *)Buffer;\r
\r
- EssData = Buffer;\r
//\r
- // We don't plan to replace IDT table with a new one, but we should not assume\r
- // the AP's IDT is the same as BSP's IDT either.\r
+ // This may be called twice for each Cpu. Only run InitializeSeparateExceptionStacks\r
+ // if this is the first call or the first call failed because of size too small.\r
//\r
- AsmReadIdtr (&Idtr);\r
- EssData->Ia32.IdtTable = (VOID *)Idtr.Base;\r
- EssData->Ia32.IdtTableSize = Idtr.Limit + 1;\r
- Status = InitializeCpuExceptionHandlersEx (NULL, EssData);\r
- ASSERT_EFI_ERROR (Status);\r
+ if ((SwitchStackData[Index].Status == EFI_NOT_STARTED) || (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL)) {\r
+ SwitchStackData[Index].Status = InitializeSeparateExceptionStacks (SwitchStackData[Index].Buffer, &SwitchStackData[Index].BufferSize);\r
+ }\r
}\r
\r
/**\r
Initializes MP exceptions handlers for the sake of stack switch requirement.\r
\r
This function will allocate required resources required to setup stack switch\r
- and pass them through CPU_EXCEPTION_INIT_DATA to each logic processor.\r
+ and pass them through SwitchStackData to each logic processor.\r
\r
**/\r
VOID\r
)\r
{\r
UINTN Index;\r
- UINTN Bsp;\r
- UINTN ExceptionNumber;\r
- UINTN OldGdtSize;\r
- UINTN NewGdtSize;\r
- UINTN NewStackSize;\r
- IA32_DESCRIPTOR Gdtr;\r
- CPU_EXCEPTION_INIT_DATA EssData;\r
- UINT8 *GdtBuffer;\r
- UINT8 *StackTop;\r
-\r
- if (!PcdGetBool (PcdCpuStackGuard)) {\r
- return;\r
- }\r
-\r
- ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList);\r
- NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber;\r
-\r
- StackTop = AllocateRuntimeZeroPool (NewStackSize * mNumberOfProcessors);\r
- ASSERT (StackTop != NULL);\r
- StackTop += NewStackSize * mNumberOfProcessors;\r
-\r
- //\r
- // The default exception handlers must have been initialized. Let's just skip\r
- // it in this method.\r
- //\r
- EssData.Ia32.Revision = CPU_EXCEPTION_INIT_DATA_REV;\r
- EssData.Ia32.InitDefaultHandlers = FALSE;\r
+ EXCEPTION_STACK_SWITCH_CONTEXT *SwitchStackData;\r
+ UINTN BufferSize;\r
+ EFI_STATUS Status;\r
+ UINT8 *Buffer;\r
\r
- EssData.Ia32.StackSwitchExceptions = FixedPcdGetPtr(PcdCpuStackSwitchExceptionList);\r
- EssData.Ia32.StackSwitchExceptionNumber = ExceptionNumber;\r
- EssData.Ia32.KnownGoodStackSize = FixedPcdGet32(PcdCpuKnownGoodStackSize);\r
-\r
- //\r
- // Initialize Gdtr to suppress incorrect compiler/analyzer warnings.\r
- //\r
- Gdtr.Base = 0;\r
- Gdtr.Limit = 0;\r
- MpInitLibWhoAmI (&Bsp);\r
+ SwitchStackData = AllocateZeroPool (mNumberOfProcessors * sizeof (EXCEPTION_STACK_SWITCH_CONTEXT));\r
+ ASSERT (SwitchStackData != NULL);\r
for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
//\r
- // To support stack switch, we need to re-construct GDT but not IDT.\r
+ // Because the procedure may runs multiple times, use the status EFI_NOT_STARTED\r
+ // to indicate the procedure haven't been run yet.\r
//\r
- if (Index == Bsp) {\r
- GetGdtr (&Gdtr);\r
+ SwitchStackData[Index].Status = EFI_NOT_STARTED;\r
+ }\r
+\r
+ Status = MpInitLibStartupAllCPUs (\r
+ InitializeExceptionStackSwitchHandlers,\r
+ 0,\r
+ SwitchStackData\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ BufferSize = 0;\r
+ for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
+ if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) {\r
+ ASSERT (SwitchStackData[Index].BufferSize != 0);\r
+ BufferSize += SwitchStackData[Index].BufferSize;\r
} else {\r
- //\r
- // AP might have different size of GDT from BSP.\r
- //\r
- MpInitLibStartupThisAP (GetGdtr, Index, NULL, 0, (VOID *)&Gdtr, NULL);\r
+ ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS);\r
+ ASSERT (SwitchStackData[Index].BufferSize == 0);\r
}\r
+ }\r
\r
- //\r
- // X64 needs only one TSS of current task working for all exceptions\r
- // because of its IST feature. IA32 needs one TSS for each exception\r
- // in addition to current task. Since AP is not supposed to allocate\r
- // memory, we have to do it in BSP. To simplify the code, we allocate\r
- // memory for IA32 case to cover both IA32 and X64 exception stack\r
- // switch.\r
- //\r
- // Layout of memory to allocate for each processor:\r
- // --------------------------------\r
- // | Alignment | (just in case)\r
- // --------------------------------\r
- // | |\r
- // | Original GDT |\r
- // | |\r
- // --------------------------------\r
- // | Current task descriptor |\r
- // --------------------------------\r
- // | |\r
- // | Exception task descriptors | X ExceptionNumber\r
- // | |\r
- // --------------------------------\r
- // | Current task-state segment |\r
- // --------------------------------\r
- // | |\r
- // | Exception task-state segment | X ExceptionNumber\r
- // | |\r
- // --------------------------------\r
- //\r
- OldGdtSize = Gdtr.Limit + 1;\r
- EssData.Ia32.ExceptionTssDescSize = sizeof (IA32_TSS_DESCRIPTOR) *\r
- (ExceptionNumber + 1);\r
- EssData.Ia32.ExceptionTssSize = sizeof (IA32_TASK_STATE_SEGMENT) *\r
- (ExceptionNumber + 1);\r
- NewGdtSize = sizeof (IA32_TSS_DESCRIPTOR) +\r
- OldGdtSize +\r
- EssData.Ia32.ExceptionTssDescSize +\r
- EssData.Ia32.ExceptionTssSize;\r
-\r
- GdtBuffer = AllocateRuntimeZeroPool (NewGdtSize);\r
- ASSERT (GdtBuffer != NULL);\r
+ if (BufferSize != 0) {\r
+ Buffer = AllocateRuntimeZeroPool (BufferSize);\r
+ ASSERT (Buffer != NULL);\r
+ BufferSize = 0;\r
+ for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
+ if (SwitchStackData[Index].Status == EFI_BUFFER_TOO_SMALL) {\r
+ SwitchStackData[Index].Buffer = (VOID *)(&Buffer[BufferSize]);\r
+ BufferSize += SwitchStackData[Index].BufferSize;\r
+ DEBUG ((\r
+ DEBUG_INFO,\r
+ "Buffer[cpu%lu] for InitializeExceptionStackSwitchHandlers: 0x%lX with size 0x%lX\n",\r
+ (UINT64)(UINTN)Index,\r
+ (UINT64)(UINTN)SwitchStackData[Index].Buffer,\r
+ (UINT64)(UINTN)SwitchStackData[Index].BufferSize\r
+ ));\r
+ }\r
+ }\r
\r
- //\r
- // Make sure GDT table alignment\r
- //\r
- EssData.Ia32.GdtTable = ALIGN_POINTER(GdtBuffer, sizeof (IA32_TSS_DESCRIPTOR));\r
- NewGdtSize -= ((UINT8 *)EssData.Ia32.GdtTable - GdtBuffer);\r
- EssData.Ia32.GdtTableSize = NewGdtSize;\r
-\r
- EssData.Ia32.ExceptionTssDesc = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize);\r
- EssData.Ia32.ExceptionTss = ((UINT8 *)EssData.Ia32.GdtTable + OldGdtSize +\r
- EssData.Ia32.ExceptionTssDescSize);\r
-\r
- EssData.Ia32.KnownGoodStackTop = (UINTN)StackTop;\r
- DEBUG ((DEBUG_INFO,\r
- "Exception stack top[cpu%lu]: 0x%lX\n",\r
- (UINT64)(UINTN)Index,\r
- (UINT64)(UINTN)StackTop));\r
-\r
- if (Index == Bsp) {\r
- InitializeExceptionStackSwitchHandlers (&EssData);\r
- } else {\r
- MpInitLibStartupThisAP (\r
- InitializeExceptionStackSwitchHandlers,\r
- Index,\r
- NULL,\r
- 0,\r
- (VOID *)&EssData,\r
- NULL\r
- );\r
+ Status = MpInitLibStartupAllCPUs (\r
+ InitializeExceptionStackSwitchHandlers,\r
+ 0,\r
+ SwitchStackData\r
+ );\r
+ ASSERT_EFI_ERROR (Status);\r
+ for (Index = 0; Index < mNumberOfProcessors; ++Index) {\r
+ ASSERT (SwitchStackData[Index].Status == EFI_SUCCESS);\r
}\r
+ }\r
\r
- StackTop -= NewStackSize;\r
+ FreePool (SwitchStackData);\r
+}\r
+\r
+/**\r
+ Initializes MP exceptions handlers for special features, such as Heap Guard\r
+ and Stack Guard.\r
+**/\r
+VOID\r
+InitializeMpExceptionHandlers (\r
+ VOID\r
+ )\r
+{\r
+ //\r
+ // Enable non-stop mode for #PF triggered by Heap Guard or NULL Pointer\r
+ // Detection.\r
+ //\r
+ if (HEAP_GUARD_NONSTOP_MODE || NULL_DETECTION_NONSTOP_MODE) {\r
+ RegisterCpuInterruptHandler (EXCEPT_IA32_DEBUG, DebugExceptionHandler);\r
+ RegisterCpuInterruptHandler (EXCEPT_IA32_PAGE_FAULT, PageFaultExceptionHandler);\r
+ }\r
+\r
+ //\r
+ // Setup stack switch for Stack Guard feature.\r
+ //\r
+ if (PcdGetBool (PcdCpuStackGuard)) {\r
+ InitializeMpExceptionStackSwitchHandlers ();\r
}\r
}\r
\r
VOID\r
)\r
{\r
- EFI_STATUS Status;\r
- UINTN NumberOfProcessors;\r
- UINTN NumberOfEnabledProcessors;\r
+ EFI_STATUS Status;\r
+ UINTN NumberOfProcessors;\r
+ UINTN NumberOfEnabledProcessors;\r
\r
//\r
// Wakeup APs to do initialization\r
DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));\r
\r
//\r
- // Initialize exception stack switch handlers for each logic processor.\r
+ // Initialize special exception handlers for each logic processor.\r
//\r
- InitializeMpExceptionStackSwitchHandlers ();\r
+ InitializeMpExceptionHandlers ();\r
\r
//\r
// Update CPU healthy information from Guided HOB\r
\r
Status = gBS->InstallMultipleProtocolInterfaces (\r
&mMpServiceHandle,\r
- &gEfiMpServiceProtocolGuid, &mMpServicesTemplate,\r
+ &gEfiMpServiceProtocolGuid,\r
+ &mMpServicesTemplate,\r
NULL\r
);\r
ASSERT_EFI_ERROR (Status);\r
}\r
-\r