--- /dev/null
+/** @file\r
+ Root SMI handler for VCPU hotplug SMIs.\r
+\r
+ Copyright (c) 2020, Red Hat, Inc.\r
+\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+**/\r
+\r
+#include <IndustryStandard/Q35MchIch9.h> // ICH9_APM_CNT\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 <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL\r
+#include <Uefi/UefiBaseType.h> // EFI_STATUS\r
+\r
+//\r
+// We use this protocol for accessing IO Ports.\r
+//\r
+STATIC EFI_MM_CPU_IO_PROTOCOL *mMmCpuIo;\r
+//\r
+// Represents the registration of the CPU Hotplug MMI handler.\r
+//\r
+STATIC EFI_HANDLE mDispatchHandle;\r
+\r
+\r
+/**\r
+ CPU Hotplug MMI handler function.\r
+\r
+ This is a root MMI handler.\r
+\r
+ @param[in] DispatchHandle The unique handle assigned to this handler by\r
+ EFI_MM_SYSTEM_TABLE.MmiHandlerRegister().\r
+\r
+ @param[in] Context Context passed in by\r
+ EFI_MM_SYSTEM_TABLE.MmiManage(). Due to\r
+ CpuHotplugMmi() being a root MMI handler,\r
+ Context is ASSERT()ed to be NULL.\r
+\r
+ @param[in,out] CommBuffer Ignored, due to CpuHotplugMmi() being a root\r
+ MMI handler.\r
+\r
+ @param[in,out] CommBufferSize Ignored, due to CpuHotplugMmi() being a root\r
+ MMI handler.\r
+\r
+ @retval EFI_SUCCESS The MMI was handled and the MMI\r
+ source was quiesced. When returned\r
+ by a non-root MMI handler,\r
+ EFI_SUCCESS terminates the\r
+ processing of MMI handlers in\r
+ EFI_MM_SYSTEM_TABLE.MmiManage().\r
+ For a root MMI handler (i.e., for\r
+ the present function too),\r
+ EFI_SUCCESS behaves identically to\r
+ EFI_WARN_INTERRUPT_SOURCE_QUIESCED,\r
+ as further root MMI handlers are\r
+ going to be called by\r
+ EFI_MM_SYSTEM_TABLE.MmiManage()\r
+ anyway.\r
+\r
+ @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The MMI source has been quiesced,\r
+ but other handlers should still\r
+ be called.\r
+\r
+ @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The MMI source is still pending,\r
+ and other handlers should still\r
+ be called.\r
+\r
+ @retval EFI_INTERRUPT_PENDING The MMI source could not be\r
+ quiesced.\r
+**/\r
+STATIC\r
+EFI_STATUS\r
+EFIAPI\r
+CpuHotplugMmi (\r
+ IN EFI_HANDLE DispatchHandle,\r
+ IN CONST VOID *Context OPTIONAL,\r
+ IN OUT VOID *CommBuffer OPTIONAL,\r
+ IN OUT UINTN *CommBufferSize OPTIONAL\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT8 ApmControl;\r
+\r
+ //\r
+ // Assert that we are entering this function due to our root MMI handler\r
+ // registration.\r
+ //\r
+ ASSERT (DispatchHandle == mDispatchHandle);\r
+ //\r
+ // When MmiManage() is invoked to process root MMI handlers, the caller (the\r
+ // MM Core) is expected to pass in a NULL Context. MmiManage() then passes\r
+ // the same NULL Context to individual handlers.\r
+ //\r
+ ASSERT (Context == NULL);\r
+ //\r
+ // Read the MMI command value from the APM Control Port, to see if this is an\r
+ // MMI we should care about.\r
+ //\r
+ Status = mMmCpuIo->Io.Read (mMmCpuIo, MM_IO_UINT8, ICH9_APM_CNT, 1,\r
+ &ApmControl);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: failed to read ICH9_APM_CNT: %r\n", __FUNCTION__,\r
+ Status));\r
+ //\r
+ // We couldn't even determine if the MMI was for us or not.\r
+ //\r
+ goto Fatal;\r
+ }\r
+\r
+ if (ApmControl != ICH9_APM_CNT_CPU_HOTPLUG) {\r
+ //\r
+ // The MMI is not for us.\r
+ //\r
+ return EFI_WARN_INTERRUPT_SOURCE_QUIESCED;\r
+ }\r
+\r
+ //\r
+ // We've handled this MMI.\r
+ //\r
+ return EFI_SUCCESS;\r
+\r
+Fatal:\r
+ ASSERT (FALSE);\r
+ CpuDeadLoop ();\r
+ //\r
+ // We couldn't handle this MMI.\r
+ //\r
+ return EFI_INTERRUPT_PENDING;\r
+}\r
+\r
+\r
+//\r
+// Entry point function of this driver.\r
+//\r
+EFI_STATUS\r
+EFIAPI\r
+CpuHotplugEntry (\r
+ IN EFI_HANDLE ImageHandle,\r
+ IN EFI_SYSTEM_TABLE *SystemTable\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ //\r
+ // This module should only be included when SMM support is required.\r
+ //\r
+ ASSERT (FeaturePcdGet (PcdSmmSmramRequire));\r
+ //\r
+ // This driver depends on the dynamically detected "SMRAM at default SMBASE"\r
+ // feature.\r
+ //\r
+ if (!PcdGetBool (PcdQ35SmramAtDefaultSmbase)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Errors from here on are fatal; we cannot allow the boot to proceed if we\r
+ // can't set up this driver to handle CPU hotplug.\r
+ //\r
+ // First, collect the protocols needed later. All of these protocols are\r
+ // listed in our module DEPEX.\r
+ //\r
+ Status = gMmst->MmLocateProtocol (&gEfiMmCpuIoProtocolGuid,\r
+ NULL /* Registration */, (VOID **)&mMmCpuIo);\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: locate MmCpuIo: %r\n", __FUNCTION__, Status));\r
+ goto Fatal;\r
+ }\r
+\r
+ //\r
+ // Register the handler for the CPU Hotplug MMI.\r
+ //\r
+ Status = gMmst->MmiHandlerRegister (\r
+ CpuHotplugMmi,\r
+ NULL, // HandlerType: root MMI handler\r
+ &mDispatchHandle\r
+ );\r
+ if (EFI_ERROR (Status)) {\r
+ DEBUG ((DEBUG_ERROR, "%a: MmiHandlerRegister(): %r\n", __FUNCTION__,\r
+ Status));\r
+ goto Fatal;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+Fatal:\r
+ ASSERT (FALSE);\r
+ CpuDeadLoop ();\r
+ return Status;\r
+}\r