From 17cb8ddba39b58e008a58aa6d502856d61f82dc9 Mon Sep 17 00:00:00 2001 From: Laszlo Ersek Date: Wed, 26 Feb 2020 23:11:50 +0100 Subject: [PATCH] OvmfPkg/CpuHotplugSmm: collect CPUs with events MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Call QemuCpuhpCollectApicIds() in the root MMI handler. The APIC IDs of the hotplugged CPUs will be used for several purposes in subsequent patches. For calling QemuCpuhpCollectApicIds(), pre-allocate both of its output arrays "PluggedApicIds" and "ToUnplugApicIds" in the driver's entry point function. The allocation size is dictated by the possible CPU count, which we fetch from "CPU_HOT_PLUG_DATA.ArrayLength". The CPU_HOT_PLUG_DATA structure in SMRAM is an out-of-band information channel between this driver and PiSmmCpuDxeSmm, underlying EFI_SMM_CPU_SERVICE_PROTOCOL. In order to consume "CPU_HOT_PLUG_DATA.ArrayLength", extend the driver's DEPEX to EFI_SMM_CPU_SERVICE_PROTOCOL. PiSmmCpuDxeSmm stores the address of CPU_HOT_PLUG_DATA to "PcdCpuHotPlugDataAddress", before it produces EFI_SMM_CPU_SERVICE_PROTOCOL. Stash the protocol at once, as it will be needed later. 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-11-lersek@redhat.com> Reviewed-by: Ard Biesheuvel Tested-by: Boris Ostrovsky --- OvmfPkg/CpuHotplugSmm/CpuHotplug.c | 111 +++++++++++++++++++++++- OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf | 7 +- 2 files changed, 115 insertions(+), 3 deletions(-) diff --git a/OvmfPkg/CpuHotplugSmm/CpuHotplug.c b/OvmfPkg/CpuHotplugSmm/CpuHotplug.c index 5df8c689c6..42e023cb85 100644 --- a/OvmfPkg/CpuHotplugSmm/CpuHotplug.c +++ b/OvmfPkg/CpuHotplugSmm/CpuHotplug.c @@ -6,15 +6,19 @@ SPDX-License-Identifier: BSD-2-Clause-Patent **/ +#include // CPU_HOT_PLUG_DATA #include // ICH9_APM_CNT #include // QEMU_CPUHP_CMD_GET_PENDING #include // CpuDeadLoop() #include // ASSERT() #include // gMmst #include // PcdGetBool() +#include // SafeUintnSub() #include // EFI_MM_CPU_IO_PROTOCOL +#include // EFI_SMM_CPU_SERVICE_PROTOCOL #include // EFI_STATUS +#include "ApicId.h" // APIC_ID #include "QemuCpuhp.h" // QemuCpuhpWriteCpuSelector() // @@ -22,6 +26,32 @@ // STATIC EFI_MM_CPU_IO_PROTOCOL *mMmCpuIo; // +// The following protocol is used to report the addition or removal of a CPU to +// the SMM CPU driver (PiSmmCpuDxeSmm). +// +STATIC EFI_SMM_CPU_SERVICE_PROTOCOL *mMmCpuService; +// +// This structure is a communication side-channel between the +// EFI_SMM_CPU_SERVICE_PROTOCOL consumer (i.e., this driver) and provider +// (i.e., PiSmmCpuDxeSmm). +// +STATIC CPU_HOT_PLUG_DATA *mCpuHotPlugData; +// +// SMRAM arrays for fetching the APIC IDs of processors with pending events (of +// known event types), for the time of just one MMI. +// +// The lifetimes of these arrays match that of this driver only because we +// don't want to allocate SMRAM at OS runtime, and potentially fail (or +// fragment the SMRAM map). +// +// These arrays provide room for ("possible CPU count" minus one) APIC IDs +// each, as we don't expect every possible CPU to appear, or disappear, in a +// single MMI. The numbers of used (populated) elements in the arrays are +// determined on every MMI separately. +// +STATIC APIC_ID *mPluggedApicIds; +STATIC APIC_ID *mToUnplugApicIds; +// // Represents the registration of the CPU Hotplug MMI handler. // STATIC EFI_HANDLE mDispatchHandle; @@ -84,6 +114,8 @@ CpuHotplugMmi ( { EFI_STATUS Status; UINT8 ApmControl; + UINT32 PluggedCount; + UINT32 ToUnplugCount; // // Assert that we are entering this function due to our root MMI handler @@ -118,6 +150,27 @@ CpuHotplugMmi ( return EFI_WARN_INTERRUPT_SOURCE_QUIESCED; } + // + // Collect the CPUs with pending events. + // + Status = QemuCpuhpCollectApicIds ( + mMmCpuIo, + mCpuHotPlugData->ArrayLength, // PossibleCpuCount + mCpuHotPlugData->ArrayLength - 1, // ApicIdCount + mPluggedApicIds, + &PluggedCount, + mToUnplugApicIds, + &ToUnplugCount + ); + if (EFI_ERROR (Status)) { + goto Fatal; + } + if (ToUnplugCount > 0) { + DEBUG ((DEBUG_ERROR, "%a: hot-unplug is not supported yet\n", + __FUNCTION__)); + goto Fatal; + } + // // We've handled this MMI. // @@ -144,6 +197,7 @@ CpuHotplugEntry ( ) { EFI_STATUS Status; + UINTN Size; // // This module should only be included when SMM support is required. @@ -170,6 +224,51 @@ CpuHotplugEntry ( DEBUG ((DEBUG_ERROR, "%a: locate MmCpuIo: %r\n", __FUNCTION__, Status)); goto Fatal; } + Status = gMmst->MmLocateProtocol (&gEfiSmmCpuServiceProtocolGuid, + NULL /* Registration */, (VOID **)&mMmCpuService); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: locate MmCpuService: %r\n", __FUNCTION__, + Status)); + goto Fatal; + } + + // + // Our DEPEX on EFI_SMM_CPU_SERVICE_PROTOCOL guarantees that PiSmmCpuDxeSmm + // has pointed PcdCpuHotPlugDataAddress to CPU_HOT_PLUG_DATA in SMRAM. + // + mCpuHotPlugData = (VOID *)(UINTN)PcdGet64 (PcdCpuHotPlugDataAddress); + if (mCpuHotPlugData == NULL) { + Status = EFI_NOT_FOUND; + DEBUG ((DEBUG_ERROR, "%a: CPU_HOT_PLUG_DATA: %r\n", __FUNCTION__, Status)); + goto Fatal; + } + // + // If the possible CPU count is 1, there's nothing for this driver to do. + // + if (mCpuHotPlugData->ArrayLength == 1) { + return EFI_UNSUPPORTED; + } + // + // Allocate the data structures that depend on the possible CPU count. + // + if (RETURN_ERROR (SafeUintnSub (mCpuHotPlugData->ArrayLength, 1, &Size)) || + RETURN_ERROR (SafeUintnMult (sizeof (APIC_ID), Size, &Size))) { + Status = EFI_ABORTED; + DEBUG ((DEBUG_ERROR, "%a: invalid CPU_HOT_PLUG_DATA\n", __FUNCTION__)); + goto Fatal; + } + Status = gMmst->MmAllocatePool (EfiRuntimeServicesData, Size, + (VOID **)&mPluggedApicIds); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: MmAllocatePool(): %r\n", __FUNCTION__, Status)); + goto Fatal; + } + Status = gMmst->MmAllocatePool (EfiRuntimeServicesData, Size, + (VOID **)&mToUnplugApicIds); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_ERROR, "%a: MmAllocatePool(): %r\n", __FUNCTION__, Status)); + goto ReleasePluggedApicIds; + } // // Sanity-check the CPU hotplug interface. @@ -200,7 +299,7 @@ CpuHotplugEntry ( Status = EFI_NOT_FOUND; DEBUG ((DEBUG_ERROR, "%a: modern CPU hotplug interface: %r\n", __FUNCTION__, Status)); - goto Fatal; + goto ReleaseToUnplugApicIds; } // @@ -214,11 +313,19 @@ CpuHotplugEntry ( if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "%a: MmiHandlerRegister(): %r\n", __FUNCTION__, Status)); - goto Fatal; + goto ReleaseToUnplugApicIds; } return EFI_SUCCESS; +ReleaseToUnplugApicIds: + gMmst->MmFreePool (mToUnplugApicIds); + mToUnplugApicIds = NULL; + +ReleasePluggedApicIds: + gMmst->MmFreePool (mPluggedApicIds); + mPluggedApicIds = NULL; + Fatal: ASSERT (FALSE); CpuDeadLoop (); diff --git a/OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf b/OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf index ab690a9e5e..31c1ee1c9f 100644 --- a/OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf +++ b/OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf @@ -30,22 +30,27 @@ [Packages] MdePkg/MdePkg.dec OvmfPkg/OvmfPkg.dec + UefiCpuPkg/UefiCpuPkg.dec [LibraryClasses] BaseLib DebugLib MmServicesTableLib PcdLib + SafeIntLib UefiDriverEntryPoint [Protocols] gEfiMmCpuIoProtocolGuid ## CONSUMES + gEfiSmmCpuServiceProtocolGuid ## CONSUMES [Pcd] + gUefiCpuPkgTokenSpaceGuid.PcdCpuHotPlugDataAddress ## CONSUMES gUefiOvmfPkgTokenSpaceGuid.PcdQ35SmramAtDefaultSmbase ## CONSUMES [FeaturePcd] gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire ## CONSUMES [Depex] - gEfiMmCpuIoProtocolGuid + gEfiMmCpuIoProtocolGuid AND + gEfiSmmCpuServiceProtocolGuid -- 2.39.2