}\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
+/**\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
+VOID\r
+EFIAPI\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
+ 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
+\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
+ for (Index = 0; Index < mNumberOfProcessors; ++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
+ GdtBuffer = AllocateRuntimeZeroPool (NewGdtSize);\r
+ ASSERT (GdtBuffer != NULL);\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
Initialize Multi-processor support.\r
\r
\r
MpInitLibGetNumberOfProcessors (&NumberOfProcessors, &NumberOfEnabledProcessors);\r
mNumberOfProcessors = NumberOfProcessors;\r
- DEBUG ((DEBUG_ERROR, "Detect CPU count: %d\n", mNumberOfProcessors));\r
+ DEBUG ((DEBUG_INFO, "Detect CPU count: %d\n", mNumberOfProcessors));\r
+\r
+ //\r
+ // Initialize exception stack switch handlers for each logic processor.\r
+ //\r
+ InitializeMpExceptionStackSwitchHandlers ();\r
\r
//\r
// Update CPU healthy information from Guided HOB\r