From 17efae27acaf00856ef648d85e4656bce5cc6bcd Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Wed, 26 Feb 2020 23:11:46 +0100 Subject: [PATCH] OvmfPkg/CpuHotplugSmm: introduce skeleton for CPU Hotplug SMM driver MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Add a new SMM driver skeleton that registers a root SMI handler, and checks if the SMI control value (written to 0xB2) indicates a CPU hotplug SMI. QEMU's ACPI payload will cause the OS to raise a broadcast SMI when a CPU hotplug event occurs, namely by writing value 4 to IO Port 0xB2. In other words, control value 4 is now allocated for this purpose; introduce the ICH9_APM_CNT_CPU_HOTPLUG macro for it. The standard identifiers in this driver use the new MM (Management Mode) terminology from the PI spec, not the earlier SMM (System Management Mode) terms. Cc: Ard Biesheuvel Cc: Igor Mammedov Cc: Jiewen Yao Cc: Jordan Justen Cc: Michael Kinney Cc: Philippe Mathieu-Daudé Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512 Signed-off-by: Laszlo Ersek Message-Id: <20200226221156.29589-7-lersek@redhat.com> Reviewed-by: Ard Biesheuvel Tested-by: Boris Ostrovsky --- OvmfPkg/CpuHotplugSmm/CpuHotplug.c | 191 ++++++++++++++++++ OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf | 48 +++++ OvmfPkg/Include/IndustryStandard/Q35MchIch9.h | 5 +- OvmfPkg/OvmfPkgIa32.dsc | 1 + OvmfPkg/OvmfPkgIa32.fdf | 1 + OvmfPkg/OvmfPkgIa32X64.dsc | 1 + OvmfPkg/OvmfPkgIa32X64.fdf | 1 + OvmfPkg/OvmfPkgX64.dsc | 1 + OvmfPkg/OvmfPkgX64.fdf | 1 + 9 files changed, 248 insertions(+), 2 deletions(-) create mode 100644 OvmfPkg/CpuHotplugSmm/CpuHotplug.c create mode 100644 OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf diff --git a/OvmfPkg/CpuHotplugSmm/CpuHotplug.c b/OvmfPkg/CpuHotplugSmm/CpuHotplug.c new file mode 100644 index 0000000000..fd09403eab --- /dev/null +++ b/OvmfPkg/CpuHotplugSmm/CpuHotplug.c @@ -0,0 +1,191 @@ +/** @file + Root SMI handler for VCPU hotplug SMIs. + + Copyright (c) 2020, Red Hat, Inc. + + SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include // ICH9_APM_CNT +#include // CpuDeadLoop() +#include // ASSERT() +#include // gMmst +#include // PcdGetBool() +#include // EFI_MM_CPU_IO_PROTOCOL +#include // EFI_STATUS + +// +// We use this protocol for accessing IO Ports. +// +STATIC EFI_MM_CPU_IO_PROTOCOL *mMmCpuIo; +// +// Represents the registration of the CPU Hotplug MMI handler. +// +STATIC EFI_HANDLE mDispatchHandle; + + +/** + CPU Hotplug MMI handler function. + + This is a root MMI handler. + + @param[in] DispatchHandle The unique handle assigned to this handler by + EFI_MM_SYSTEM_TABLE.MmiHandlerRegister(). + + @param[in] Context Context passed in by + EFI_MM_SYSTEM_TABLE.MmiManage(). Due to + CpuHotplugMmi() being a root MMI handler, + Context is ASSERT()ed to be NULL. + + @param[in,out] CommBuffer Ignored, due to CpuHotplugMmi() being a root + MMI handler. + + @param[in,out] CommBufferSize Ignored, due to CpuHotplugMmi() being a root + MMI handler. + + @retval EFI_SUCCESS The MMI was handled and the MMI + source was quiesced. When returned + by a non-root MMI handler, + EFI_SUCCESS terminates the + processing of MMI handlers in + EFI_MM_SYSTEM_TABLE.MmiManage(). + For a root MMI handler (i.e., for + the present function too), + EFI_SUCCESS behaves identically to + EFI_WARN_INTERRUPT_SOURCE_QUIESCED, + as further root MMI handlers are + going to be called by + EFI_MM_SYSTEM_TABLE.MmiManage() + anyway. + + @retval EFI_WARN_INTERRUPT_SOURCE_QUIESCED The MMI source has been quiesced, + but other handlers should still + be called. + + @retval EFI_WARN_INTERRUPT_SOURCE_PENDING The MMI source is still pending, + and other handlers should still + be called. + + @retval EFI_INTERRUPT_PENDING The MMI source could not be + quiesced. +**/ +STATIC +EFI_STATUS +EFIAPI +CpuHotplugMmi ( + IN EFI_HANDLE DispatchHandle, + IN CONST VOID *Context OPTIONAL, + IN OUT VOID *CommBuffer OPTIONAL, + IN OUT UINTN *CommBufferSize OPTIONAL + ) +{ + EFI_STATUS Status; + UINT8 ApmControl; + + // + // Assert that we are entering this function due to our root MMI handler + // registration. + // + ASSERT (DispatchHandle == mDispatchHandle); + // + // When MmiManage() is invoked to process root MMI handlers, the caller (the + // MM Core) is expected to pass in a NULL Context. MmiManage() then passes + // the same NULL Context to individual handlers. + // + ASSERT (Context == NULL); + // + // Read the MMI command value from the APM Control Port, to see if this is an + // MMI we should care about. + // + Status = mMmCpuIo->Io.Read (mMmCpuIo, MM_IO_UINT8, ICH9_APM_CNT, 1, + &ApmControl); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: failed to read ICH9_APM_CNT: %r\n", __FUNCTION__, + Status)); + // + // We couldn't even determine if the MMI was for us or not. + // + goto Fatal; + } + + if (ApmControl != ICH9_APM_CNT_CPU_HOTPLUG) { + // + // The MMI is not for us. + // + return EFI_WARN_INTERRUPT_SOURCE_QUIESCED; + } + + // + // We've handled this MMI. + // + return EFI_SUCCESS; + +Fatal: + ASSERT (FALSE); + CpuDeadLoop (); + // + // We couldn't handle this MMI. + // + return EFI_INTERRUPT_PENDING; +} + + +// +// Entry point function of this driver. +// +EFI_STATUS +EFIAPI +CpuHotplugEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + // + // This module should only be included when SMM support is required. + // + ASSERT (FeaturePcdGet (PcdSmmSmramRequire)); + // + // This driver depends on the dynamically detected "SMRAM at default SMBASE" + // feature. + // + if (!PcdGetBool (PcdQ35SmramAtDefaultSmbase)) { + return EFI_UNSUPPORTED; + } + + // + // Errors from here on are fatal; we cannot allow the boot to proceed if we + // can't set up this driver to handle CPU hotplug. + // + // First, collect the protocols needed later. All of these protocols are + // listed in our module DEPEX. + // + Status = gMmst->MmLocateProtocol (&gEfiMmCpuIoProtocolGuid, + NULL /* Registration */, (VOID **)&mMmCpuIo); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: locate MmCpuIo: %r\n", __FUNCTION__, Status)); + goto Fatal; + } + + // + // Register the handler for the CPU Hotplug MMI. + // + Status = gMmst->MmiHandlerRegister ( + CpuHotplugMmi, + NULL, // HandlerType: root MMI handler + &mDispatchHandle + ); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: MmiHandlerRegister(): %r\n", __FUNCTION__, + Status)); + goto Fatal; + } + + return EFI_SUCCESS; + +Fatal: + ASSERT (FALSE); + CpuDeadLoop (); + return Status; +} diff --git a/OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf b/OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf new file mode 100644 index 0000000000..fa70858a8d --- /dev/null +++ b/OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf @@ -0,0 +1,48 @@ +## @file +# Root SMI handler for VCPU hotplug SMIs. +# +# Copyright (c) 2020, Red Hat, Inc. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +## + +[Defines] + INF_VERSION = 1.29 + PI_SPECIFICATION_VERSION = 0x00010046 # PI-1.7.0 + BASE_NAME = CpuHotplugSmm + FILE_GUID = 84EEA114-C6BE-4445-8F90-51D97863E363 + MODULE_TYPE = DXE_SMM_DRIVER + ENTRY_POINT = CpuHotplugEntry + +# +# The following information is for reference only and not required by the build +# tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + CpuHotplug.c + +[Packages] + MdePkg/MdePkg.dec + OvmfPkg/OvmfPkg.dec + +[LibraryClasses] + BaseLib + DebugLib + MmServicesTableLib + PcdLib + UefiDriverEntryPoint + +[Protocols] + gEfiMmCpuIoProtocolGuid ## CONSUMES + +[Pcd] + gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase ## CONSUMES + +[FeaturePcd] + gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire ## CONSUMES + +[Depex] + gEfiMmCpuIoProtocolGuid diff --git a/OvmfPkg/Include/IndustryStandard/Q35MchIch9.h b/OvmfPkg/Include/IndustryStandard/Q35MchIch9.h index cb705fee92..73db4b59a1 100644 --- a/OvmfPkg/Include/IndustryStandard/Q35MchIch9.h +++ b/OvmfPkg/Include/IndustryStandard/Q35MchIch9.h @@ -109,8 +109,9 @@ // // IO ports // -#define ICH9_APM_CNT 0xB2 -#define ICH9_APM_STS 0xB3 +#define ICH9_APM_CNT 0xB2 +#define ICH9_APM_CNT_CPU_HOTPLUG 0x04 +#define ICH9_APM_STS 0xB3 #define ICH9_CPU_HOTPLUG_BASE 0x0CD8 diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc index 25e704be10..7a8a49af95 100644 --- a/OvmfPkg/OvmfPkgIa32.dsc +++ b/OvmfPkg/OvmfPkgIa32.dsc @@ -880,6 +880,7 @@ # # Privileged drivers (DXE_SMM_DRIVER modules) # + OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf { diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf index 2e12807282..d58214eb80 100644 --- a/OvmfPkg/OvmfPkgIa32.fdf +++ b/OvmfPkg/OvmfPkgIa32.fdf @@ -322,6 +322,7 @@ INF OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf INF UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.inf INF MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf INF MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf +INF OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf INF UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf INF MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf INF UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc index 6def3f0c20..67c1635e33 100644 --- a/OvmfPkg/OvmfPkgIa32X64.dsc +++ b/OvmfPkg/OvmfPkgIa32X64.dsc @@ -893,6 +893,7 @@ # # Privileged drivers (DXE_SMM_DRIVER modules) # + OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf { diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf index d67d336876..c21c3b92d8 100644 --- a/OvmfPkg/OvmfPkgIa32X64.fdf +++ b/OvmfPkg/OvmfPkgIa32X64.fdf @@ -329,6 +329,7 @@ INF OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf INF UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.inf INF MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf INF MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf +INF OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf INF UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf INF MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf INF UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc index 2c1902f9f2..45014190a6 100644 --- a/OvmfPkg/OvmfPkgX64.dsc +++ b/OvmfPkg/OvmfPkgX64.dsc @@ -891,6 +891,7 @@ # # Privileged drivers (DXE_SMM_DRIVER modules) # + OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf { diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf index d67d336876..c21c3b92d8 100644 --- a/OvmfPkg/OvmfPkgX64.fdf +++ b/OvmfPkg/OvmfPkgX64.fdf @@ -329,6 +329,7 @@ INF OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf INF UefiCpuPkg/CpuS3DataDxe/CpuS3DataDxe.inf INF MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf INF MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf +INF OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf INF UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf INF MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf INF UefiCpuPkg/PiSmmCpuDxeSmm/PiSmmCpuDxeSmm.inf -- 2.39.2