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
{\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
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
)\r
{\r
EFI_STATUS Status;\r
+ UINTN Size;\r
\r
//\r
// This module should only be included when SMM support is required.\r
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
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
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