/** @file\r
CPU PEI Module installs CPU Multiple Processor PPI.\r
\r
- Copyright (c) 2015 - 2016, 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) 2015 - 2018, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
\r
**/\r
\r
}\r
\r
/**\r
- The Entry point of the MP CPU PEIM.\r
+ Get GDT register value.\r
\r
- This function will wakeup APs and collect CPU AP count and install the\r
- Mp Service Ppi.\r
+ This function is mainly for AP purpose because AP may have different GDT\r
+ table than BSP.\r
\r
- @param FileHandle Handle of the file being invoked.\r
- @param PeiServices Describes the list of possible PEI Services.\r
+ @param[in,out] Buffer The pointer to private data buffer.\r
\r
- @retval EFI_SUCCESS MpServicePpi is installed successfully.\r
+**/\r
+VOID\r
+EFIAPI\r
+GetGdtr (\r
+ IN OUT VOID *Buffer\r
+ )\r
+{\r
+ AsmReadGdtr ((IA32_DESCRIPTOR *)Buffer);\r
+}\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
+ 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
\r
**/\r
-EFI_STATUS\r
+VOID\r
EFIAPI\r
-CpuMpPeimInit (\r
- IN EFI_PEI_FILE_HANDLE FileHandle,\r
+InitializeExceptionStackSwitchHandlers (\r
+ IN OUT VOID *Buffer\r
+ )\r
+{\r
+ CPU_EXCEPTION_INIT_DATA *EssData;\r
+ IA32_DESCRIPTOR Idtr;\r
+ EFI_STATUS Status;\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
+ //\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
+}\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
+\r
+**/\r
+VOID\r
+InitializeMpExceptionStackSwitchHandlers (\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\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
+ UINTN NumberOfProcessors;\r
+\r
+ if (!PcdGetBool (PcdCpuStackGuard)) {\r
+ return;\r
+ }\r
+\r
+ MpInitLibGetNumberOfProcessors(&NumberOfProcessors, NULL);\r
+ MpInitLibWhoAmI (&Bsp);\r
+\r
+ ExceptionNumber = FixedPcdGetSize (PcdCpuStackSwitchExceptionList);\r
+ NewStackSize = FixedPcdGet32 (PcdCpuKnownGoodStackSize) * ExceptionNumber;\r
+\r
+ Status = PeiServicesAllocatePool (\r
+ NewStackSize * NumberOfProcessors,\r
+ (VOID **)&StackTop\r
+ );\r
+ ASSERT(StackTop != NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return;\r
+ }\r
+ StackTop += NewStackSize * NumberOfProcessors;\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
+\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
+ for (Index = 0; Index < NumberOfProcessors; ++Index) {\r
+ //\r
+ // To support stack switch, we need to re-construct GDT but not IDT.\r
+ //\r
+ if (Index == Bsp) {\r
+ GetGdtr(&Gdtr);\r
+ } else {\r
+ //\r
+ // AP might have different size of GDT from BSP.\r
+ //\r
+ MpInitLibStartupThisAP (GetGdtr, Index, NULL, 0, (VOID *)&Gdtr, NULL);\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
+ Status = PeiServicesAllocatePool (\r
+ NewGdtSize,\r
+ (VOID **)&GdtBuffer\r
+ );\r
+ ASSERT (GdtBuffer != NULL);\r
+ if (EFI_ERROR (Status)) {\r
+ ASSERT_EFI_ERROR (Status);\r
+ return;\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
+ }\r
+\r
+ StackTop -= NewStackSize;\r
+ }\r
+}\r
+\r
+/**\r
+ Initializes MP and exceptions handlers.\r
+\r
+ @param PeiServices The pointer to the PEI Services Table.\r
+\r
+ @retval EFI_SUCCESS MP was successfully initialized.\r
+ @retval others Error occurred in MP initialization.\r
+\r
+**/\r
+EFI_STATUS\r
+InitializeCpuMpWorker (\r
IN CONST EFI_PEI_SERVICES **PeiServices\r
)\r
{\r
- EFI_STATUS Status;\r
+ EFI_STATUS Status;\r
EFI_VECTOR_HANDOFF_INFO *VectorInfo;\r
EFI_PEI_VECTOR_HANDOFF_INFO_PPI *VectorHandoffInfoPpi;\r
\r
if (Status == EFI_SUCCESS) {\r
VectorInfo = VectorHandoffInfoPpi->Info;\r
}\r
- Status = InitializeCpuExceptionHandlers (VectorInfo);\r
- ASSERT_EFI_ERROR (Status);\r
- \r
+\r
//\r
- // Wakeup APs to do initialization\r
+ // Initialize default handlers\r
//\r
+ Status = InitializeCpuExceptionHandlers (VectorInfo);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
Status = MpInitLibInitialize ();\r
- ASSERT_EFI_ERROR (Status);\r
+ if (EFI_ERROR (Status)) {\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Special initialization for the sake of Stack Guard\r
+ //\r
+ InitializeMpExceptionStackSwitchHandlers ();\r
\r
//\r
// Update and publish CPU BIST information\r
\r
return Status;\r
}\r
+\r
+/**\r
+ The Entry point of the MP CPU PEIM.\r
+\r
+ This function will wakeup APs and collect CPU AP count and install the\r
+ Mp Service Ppi.\r
+\r
+ @param FileHandle Handle of the file being invoked.\r
+ @param PeiServices Describes the list of possible PEI Services.\r
+\r
+ @retval EFI_SUCCESS MpServicePpi is installed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CpuMpPeimInit (\r
+ IN EFI_PEI_FILE_HANDLE FileHandle,\r
+ IN CONST EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // For the sake of special initialization needing to be done right after\r
+ // memory discovery.\r
+ //\r
+ Status = PeiServicesNotifyPpi (&mPostMemNotifyList[0]);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ return Status;\r
+}\r