]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/CpuDxe/CpuMp.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / CpuDxe / CpuMp.c
index 56ba02615203ea560686a2c982b13608b92802f3..e7575d9b8062e77b4a1957338d32d9679d258cca 100644 (file)
@@ -1,22 +1,16 @@
 /** @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
@@ -170,7 +164,7 @@ GetProcessorInfo (
        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
@@ -287,7 +281,7 @@ StartupAllAPs (
   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
@@ -515,7 +509,7 @@ WhoAmI (
   OUT UINTN                    *ProcessorNumber\r
   )\r
 {\r
-  return MpInitLibWhoAmI (ProcessorNumber);;\r
+  return MpInitLibWhoAmI (ProcessorNumber);\r
 }\r
 \r
 /**\r
@@ -553,8 +547,8 @@ CollectBistDataFromHob (
     // 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
@@ -562,14 +556,14 @@ CollectBistDataFromHob (
     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
@@ -589,6 +583,7 @@ CollectBistDataFromHob (
         BistData = CpuInstance[CpuInstanceNumber].InfoRecord.IA32HealthFlags;\r
       }\r
     }\r
+\r
     if (BistData.Uint32 != 0) {\r
       //\r
       // Report Status Code that self test is failed\r
@@ -601,28 +596,19 @@ CollectBistDataFromHob (
   }\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
@@ -631,30 +617,29 @@ GetGdtr (
 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
@@ -663,125 +648,94 @@ InitializeMpExceptionStackSwitchHandlers (
   )\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
+  EXCEPTION_STACK_SWITCH_CONTEXT  *SwitchStackData;\r
+  UINTN                           BufferSize;\r
+  EFI_STATUS                      Status;\r
+  UINT8                           *Buffer;\r
 \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
+  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
-    StackTop  -= NewStackSize;\r
+  //\r
+  // Setup stack switch for Stack Guard feature.\r
+  //\r
+  if (PcdGetBool (PcdCpuStackGuard)) {\r
+    InitializeMpExceptionStackSwitchHandlers ();\r
   }\r
 }\r
 \r
@@ -794,9 +748,9 @@ InitializeMpSupport (
   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
@@ -809,9 +763,9 @@ InitializeMpSupport (
   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
@@ -820,9 +774,9 @@ InitializeMpSupport (
 \r
   Status = gBS->InstallMultipleProtocolInterfaces (\r
                   &mMpServiceHandle,\r
-                  &gEfiMpServiceProtocolGuid,  &mMpServicesTemplate,\r
+                  &gEfiMpServiceProtocolGuid,\r
+                  &mMpServicesTemplate,\r
                   NULL\r
                   );\r
   ASSERT_EFI_ERROR (Status);\r
 }\r
-\r