summaryrefslogtreecommitdiffstats
path: root/OvmfPkg/CpuHotplugSmm/CpuHotplug.c
diff options
context:
space:
mode:
authorLaszlo Ersek <lersek@redhat.com>2020-02-26 23:11:50 +0100
committermergify[bot] <37929162+mergify[bot]@users.noreply.github.com>2020-03-04 12:22:07 +0000
commit17cb8ddba39b58e008a58aa6d502856d61f82dc9 (patch)
tree653845332283c9ab7e4dbe12553fecc6bc35442e /OvmfPkg/CpuHotplugSmm/CpuHotplug.c
parent763840c9abc8d48567498edf00e2f31a0220184f (diff)
downloadedk2-17cb8ddba39b58e008a58aa6d502856d61f82dc9.tar.gz
edk2-17cb8ddba39b58e008a58aa6d502856d61f82dc9.tar.bz2
edk2-17cb8ddba39b58e008a58aa6d502856d61f82dc9.zip
OvmfPkg/CpuHotplugSmm: collect CPUs with events
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 <ard.biesheuvel@linaro.org> Cc: Igor Mammedov <imammedo@redhat.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200226221156.29589-11-lersek@redhat.com> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Tested-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Diffstat (limited to 'OvmfPkg/CpuHotplugSmm/CpuHotplug.c')
-rw-r--r--OvmfPkg/CpuHotplugSmm/CpuHotplug.c111
1 files changed, 109 insertions, 2 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 <CpuHotPlugData.h> // CPU_HOT_PLUG_DATA
#include <IndustryStandard/Q35MchIch9.h> // ICH9_APM_CNT
#include <IndustryStandard/QemuCpuHotplug.h> // QEMU_CPUHP_CMD_GET_PENDING
#include <Library/BaseLib.h> // CpuDeadLoop()
#include <Library/DebugLib.h> // ASSERT()
#include <Library/MmServicesTableLib.h> // gMmst
#include <Library/PcdLib.h> // PcdGetBool()
+#include <Library/SafeIntLib.h> // SafeUintnSub()
#include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL
+#include <Protocol/SmmCpuService.h> // EFI_SMM_CPU_SERVICE_PROTOCOL
#include <Uefi/UefiBaseType.h> // 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
@@ -119,6 +151,27 @@ CpuHotplugMmi (
}
//
+ // 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.
//
return EFI_SUCCESS;
@@ -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 ();