]> git.proxmox.com Git - mirror_edk2.git/blobdiff - OvmfPkg/CpuHotplugSmm/CpuHotplug.c
OvmfPkg/CpuHotplugSmm: collect CPUs with events
[mirror_edk2.git] / OvmfPkg / CpuHotplugSmm / CpuHotplug.c
index 5df8c689c63aac0ed4d4b3810d790c113bde5722..42e023cb85c0bff46009978b550fb428b5c2b7ac 100644 (file)
@@ -6,15 +6,19 @@
   SPDX-License-Identifier: BSD-2-Clause-Patent\r
 **/\r
 \r
+#include <CpuHotPlugData.h>                  // CPU_HOT_PLUG_DATA\r
 #include <IndustryStandard/Q35MchIch9.h>     // ICH9_APM_CNT\r
 #include <IndustryStandard/QemuCpuHotplug.h> // QEMU_CPUHP_CMD_GET_PENDING\r
 #include <Library/BaseLib.h>                 // CpuDeadLoop()\r
 #include <Library/DebugLib.h>                // ASSERT()\r
 #include <Library/MmServicesTableLib.h>      // gMmst\r
 #include <Library/PcdLib.h>                  // PcdGetBool()\r
+#include <Library/SafeIntLib.h>              // SafeUintnSub()\r
 #include <Protocol/MmCpuIo.h>                // EFI_MM_CPU_IO_PROTOCOL\r
+#include <Protocol/SmmCpuService.h>          // EFI_SMM_CPU_SERVICE_PROTOCOL\r
 #include <Uefi/UefiBaseType.h>               // EFI_STATUS\r
 \r
+#include "ApicId.h"                          // APIC_ID\r
 #include "QemuCpuhp.h"                       // QemuCpuhpWriteCpuSelector()\r
 \r
 //\r
 //\r
 STATIC EFI_MM_CPU_IO_PROTOCOL *mMmCpuIo;\r
 //\r
+// The following protocol is used to report the addition or removal of a CPU to\r
+// the SMM CPU driver (PiSmmCpuDxeSmm).\r
+//\r
+STATIC EFI_SMM_CPU_SERVICE_PROTOCOL *mMmCpuService;\r
+//\r
+// This structure is a communication side-channel between the\r
+// EFI_SMM_CPU_SERVICE_PROTOCOL consumer (i.e., this driver) and provider\r
+// (i.e., PiSmmCpuDxeSmm).\r
+//\r
+STATIC CPU_HOT_PLUG_DATA *mCpuHotPlugData;\r
+//\r
+// SMRAM arrays for fetching the APIC IDs of processors with pending events (of\r
+// known event types), for the time of just one MMI.\r
+//\r
+// The lifetimes of these arrays match that of this driver only because we\r
+// don't want to allocate SMRAM at OS runtime, and potentially fail (or\r
+// fragment the SMRAM map).\r
+//\r
+// These arrays provide room for ("possible CPU count" minus one) APIC IDs\r
+// each, as we don't expect every possible CPU to appear, or disappear, in a\r
+// single MMI. The numbers of used (populated) elements in the arrays are\r
+// determined on every MMI separately.\r
+//\r
+STATIC APIC_ID *mPluggedApicIds;\r
+STATIC APIC_ID *mToUnplugApicIds;\r
+//\r
 // Represents the registration of the CPU Hotplug MMI handler.\r
 //\r
 STATIC EFI_HANDLE mDispatchHandle;\r
@@ -84,6 +114,8 @@ CpuHotplugMmi (
 {\r
   EFI_STATUS Status;\r
   UINT8      ApmControl;\r
+  UINT32     PluggedCount;\r
+  UINT32     ToUnplugCount;\r
 \r
   //\r
   // Assert that we are entering this function due to our root MMI handler\r
@@ -118,6 +150,27 @@ CpuHotplugMmi (
     return EFI_WARN_INTERRUPT_SOURCE_QUIESCED;\r
   }\r
 \r
+  //\r
+  // Collect the CPUs with pending events.\r
+  //\r
+  Status = QemuCpuhpCollectApicIds (\r
+             mMmCpuIo,\r
+             mCpuHotPlugData->ArrayLength,     // PossibleCpuCount\r
+             mCpuHotPlugData->ArrayLength - 1, // ApicIdCount\r
+             mPluggedApicIds,\r
+             &PluggedCount,\r
+             mToUnplugApicIds,\r
+             &ToUnplugCount\r
+             );\r
+  if (EFI_ERROR (Status)) {\r
+    goto Fatal;\r
+  }\r
+  if (ToUnplugCount > 0) {\r
+    DEBUG ((DEBUG_ERROR, "%a: hot-unplug is not supported yet\n",\r
+      __FUNCTION__));\r
+    goto Fatal;\r
+  }\r
+\r
   //\r
   // We've handled this MMI.\r
   //\r
@@ -144,6 +197,7 @@ CpuHotplugEntry (
   )\r
 {\r
   EFI_STATUS Status;\r
+  UINTN      Size;\r
 \r
   //\r
   // This module should only be included when SMM support is required.\r
@@ -170,6 +224,51 @@ CpuHotplugEntry (
     DEBUG ((DEBUG_ERROR, "%a: locate MmCpuIo: %r\n", __FUNCTION__, Status));\r
     goto Fatal;\r
   }\r
+  Status = gMmst->MmLocateProtocol (&gEfiSmmCpuServiceProtocolGuid,\r
+                    NULL /* Registration */, (VOID **)&mMmCpuService);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: locate MmCpuService: %r\n", __FUNCTION__,\r
+      Status));\r
+    goto Fatal;\r
+  }\r
+\r
+  //\r
+  // Our DEPEX on EFI_SMM_CPU_SERVICE_PROTOCOL guarantees that PiSmmCpuDxeSmm\r
+  // has pointed PcdCpuHotPlugDataAddress to CPU_HOT_PLUG_DATA in SMRAM.\r
+  //\r
+  mCpuHotPlugData = (VOID *)(UINTN)PcdGet64 (PcdCpuHotPlugDataAddress);\r
+  if (mCpuHotPlugData == NULL) {\r
+    Status = EFI_NOT_FOUND;\r
+    DEBUG ((DEBUG_ERROR, "%a: CPU_HOT_PLUG_DATA: %r\n", __FUNCTION__, Status));\r
+    goto Fatal;\r
+  }\r
+  //\r
+  // If the possible CPU count is 1, there's nothing for this driver to do.\r
+  //\r
+  if (mCpuHotPlugData->ArrayLength == 1) {\r
+    return EFI_UNSUPPORTED;\r
+  }\r
+  //\r
+  // Allocate the data structures that depend on the possible CPU count.\r
+  //\r
+  if (RETURN_ERROR (SafeUintnSub (mCpuHotPlugData->ArrayLength, 1, &Size)) ||\r
+      RETURN_ERROR (SafeUintnMult (sizeof (APIC_ID), Size, &Size))) {\r
+    Status = EFI_ABORTED;\r
+    DEBUG ((DEBUG_ERROR, "%a: invalid CPU_HOT_PLUG_DATA\n", __FUNCTION__));\r
+    goto Fatal;\r
+  }\r
+  Status = gMmst->MmAllocatePool (EfiRuntimeServicesData, Size,\r
+                    (VOID **)&mPluggedApicIds);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: MmAllocatePool(): %r\n", __FUNCTION__, Status));\r
+    goto Fatal;\r
+  }\r
+  Status = gMmst->MmAllocatePool (EfiRuntimeServicesData, Size,\r
+                    (VOID **)&mToUnplugApicIds);\r
+  if (EFI_ERROR (Status)) {\r
+    DEBUG ((DEBUG_ERROR, "%a: MmAllocatePool(): %r\n", __FUNCTION__, Status));\r
+    goto ReleasePluggedApicIds;\r
+  }\r
 \r
   //\r
   // Sanity-check the CPU hotplug interface.\r
@@ -200,7 +299,7 @@ CpuHotplugEntry (
     Status = EFI_NOT_FOUND;\r
     DEBUG ((DEBUG_ERROR, "%a: modern CPU hotplug interface: %r\n",\r
       __FUNCTION__, Status));\r
-    goto Fatal;\r
+    goto ReleaseToUnplugApicIds;\r
   }\r
 \r
   //\r
@@ -214,11 +313,19 @@ CpuHotplugEntry (
   if (EFI_ERROR (Status)) {\r
     DEBUG ((DEBUG_ERROR, "%a: MmiHandlerRegister(): %r\n", __FUNCTION__,\r
       Status));\r
-    goto Fatal;\r
+    goto ReleaseToUnplugApicIds;\r
   }\r
 \r
   return EFI_SUCCESS;\r
 \r
+ReleaseToUnplugApicIds:\r
+  gMmst->MmFreePool (mToUnplugApicIds);\r
+  mToUnplugApicIds = NULL;\r
+\r
+ReleasePluggedApicIds:\r
+  gMmst->MmFreePool (mPluggedApicIds);\r
+  mPluggedApicIds = NULL;\r
+\r
 Fatal:\r
   ASSERT (FALSE);\r
   CpuDeadLoop ();\r