]> git.proxmox.com Git - mirror_edk2.git/blobdiff - UefiCpuPkg/CpuMpPei/CpuMpPei.c
UefiCpuPkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / UefiCpuPkg / CpuMpPei / CpuMpPei.c
index 3b72f444e9b22638be12712148322aa172de2376..926541c93712ade823158540b99f3035b6a369b5 100644 (file)
@@ -1,14 +1,8 @@
 /** @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
@@ -409,25 +403,227 @@ PeiWhoAmI (
 }\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
@@ -444,14 +640,24 @@ CpuMpPeimInit (
   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
@@ -466,3 +672,34 @@ CpuMpPeimInit (
 \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