OvmfPkg/CpuHotplugSmm: introduce skeleton for CPU Hotplug SMM driver

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 <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-7-lersek@redhat.com>
Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Tested-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
This commit is contained in:
Laszlo Ersek 2020-02-26 23:11:46 +01:00 committed by mergify[bot]
parent 49df3fcee1
commit 17efae27ac
9 changed files with 248 additions and 2 deletions

@ -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 <IndustryStandard/Q35MchIch9.h> // ICH9_APM_CNT
#include <Library/BaseLib.h> // CpuDeadLoop()
#include <Library/DebugLib.h> // ASSERT()
#include <Library/MmServicesTableLib.h> // gMmst
#include <Library/PcdLib.h> // PcdGetBool()
#include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL
#include <Uefi/UefiBaseType.h> // 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;
}

@ -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

@ -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

@ -880,6 +880,7 @@
#
# Privileged drivers (DXE_SMM_DRIVER modules)
#
OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf
UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf
MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf {
<LibraryClasses>

@ -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

@ -893,6 +893,7 @@
#
# Privileged drivers (DXE_SMM_DRIVER modules)
#
OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf
UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf
MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf {
<LibraryClasses>

@ -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

@ -891,6 +891,7 @@
#
# Privileged drivers (DXE_SMM_DRIVER modules)
#
OvmfPkg/CpuHotplugSmm/CpuHotplugSmm.inf
UefiCpuPkg/CpuIo2Smm/CpuIo2Smm.inf
MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf {
<LibraryClasses>

@ -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