summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--OvmfPkg/OvmfPkgIa32.dsc1
-rw-r--r--OvmfPkg/OvmfPkgIa32.fdf1
-rw-r--r--OvmfPkg/OvmfPkgIa32X64.dsc1
-rw-r--r--OvmfPkg/OvmfPkgIa32X64.fdf1
-rw-r--r--OvmfPkg/OvmfPkgX64.dsc1
-rw-r--r--OvmfPkg/OvmfPkgX64.fdf1
-rw-r--r--OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c366
-rw-r--r--OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf66
8 files changed, 438 insertions, 0 deletions
diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index 44773ba9ce..a95df685f0 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -645,4 +645,5 @@
!if $(SMM_REQUIRE) == TRUE
OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+ OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
!endif
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index 388d60e26c..9fabcf729b 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -357,6 +357,7 @@ INF OvmfPkg/PlatformDxe/Platform.inf
!if $(SMM_REQUIRE) == TRUE
INF OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+INF OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
!endif
################################################################################
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 10415c7303..9b071cb03a 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -652,4 +652,5 @@
!if $(SMM_REQUIRE) == TRUE
OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+ OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
!endif
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index 4babc8a7a5..c818d35863 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -357,6 +357,7 @@ INF OvmfPkg/PlatformDxe/Platform.inf
!if $(SMM_REQUIRE) == TRUE
INF OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+INF OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
!endif
################################################################################
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 3b85285e2f..3f03ca2f1e 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -650,4 +650,5 @@
!if $(SMM_REQUIRE) == TRUE
OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+ OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
!endif
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index c75115efef..3b1742d148 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -357,6 +357,7 @@ INF OvmfPkg/PlatformDxe/Platform.inf
!if $(SMM_REQUIRE) == TRUE
INF OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+INF OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
!endif
################################################################################
diff --git a/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c
new file mode 100644
index 0000000000..82549b0a7e
--- /dev/null
+++ b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c
@@ -0,0 +1,366 @@
+/** @file
+
+ A DXE_RUNTIME_DRIVER providing synchronous SMI activations via the
+ EFI_SMM_CONTROL2_PROTOCOL.
+
+ We expect the PEI phase to have covered the following:
+ - ensure that the underlying QEMU machine type be Q35
+ (responsible: OvmfPkg/SmmAccess/SmmAccessPei.inf)
+ - ensure that the ACPI PM IO space be configured
+ (responsible: OvmfPkg/PlatformPei/PlatformPei.inf)
+
+ Our own entry point is responsible for confirming the SMI feature and for
+ configuring it.
+
+ Copyright (C) 2013, 2015, Red Hat, Inc.<BR>
+ Copyright (c) 2009 - 2010, Intel Corporation. All rights reserved.<BR>
+
+ This program and the accompanying materials are licensed and made available
+ under the terms and conditions of the BSD License which accompanies this
+ distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+ WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <IndustryStandard/Q35MchIch9.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/IoLib.h>
+#include <Library/PcdLib.h>
+#include <Library/PciLib.h>
+#include <Library/QemuFwCfgLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Protocol/S3SaveState.h>
+#include <Protocol/SmmControl2.h>
+
+//
+// Forward declaration.
+//
+STATIC
+VOID
+EFIAPI
+OnS3SaveStateInstalled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ );
+
+//
+// The absolute IO port address of the SMI Control and Enable Register. It is
+// only used to carry information from the entry point function to the
+// S3SaveState protocol installation callback, strictly before the runtime
+// phase.
+//
+STATIC UINTN mSmiEnable;
+
+//
+// Event signaled when an S3SaveState protocol interface is installed.
+//
+STATIC EFI_EVENT mS3SaveStateInstalled;
+
+/**
+ Invokes SMI activation from either the preboot or runtime environment.
+
+ This function generates an SMI.
+
+ @param[in] This The EFI_SMM_CONTROL2_PROTOCOL instance.
+ @param[in,out] CommandPort The value written to the command port.
+ @param[in,out] DataPort The value written to the data port.
+ @param[in] Periodic Optional mechanism to engender a periodic
+ stream.
+ @param[in] ActivationInterval Optional parameter to repeat at this
+ period one time or, if the Periodic
+ Boolean is set, periodically.
+
+ @retval EFI_SUCCESS The SMI/PMI has been engendered.
+ @retval EFI_DEVICE_ERROR The timing is unsupported.
+ @retval EFI_INVALID_PARAMETER The activation period is unsupported.
+ @retval EFI_INVALID_PARAMETER The last periodic activation has not been
+ cleared.
+ @retval EFI_NOT_STARTED The SMM base service has not been initialized.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SmmControl2DxeTrigger (
+ IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
+ IN OUT UINT8 *CommandPort OPTIONAL,
+ IN OUT UINT8 *DataPort OPTIONAL,
+ IN BOOLEAN Periodic OPTIONAL,
+ IN UINTN ActivationInterval OPTIONAL
+ )
+{
+ //
+ // No support for queued or periodic activation.
+ //
+ if (Periodic || ActivationInterval > 0) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // The so-called "Advanced Power Management Status Port Register" is in fact
+ // a generic data passing register, between the caller and the SMI
+ // dispatcher. The ICH9 spec calls it "scratchpad register" -- calling it
+ // "status" elsewhere seems quite the misnomer. Status registers usually
+ // report about hardware status, while this register is fully governed by
+ // software.
+ //
+ // Write to the status register first, as this won't trigger the SMI just
+ // yet. Then write to the control register.
+ //
+ IoWrite8 (ICH9_APM_STS, DataPort == NULL ? 0 : *DataPort);
+ IoWrite8 (ICH9_APM_CNT, CommandPort == NULL ? 0 : *CommandPort);
+ return EFI_SUCCESS;
+}
+
+/**
+ Clears any system state that was created in response to the Trigger() call.
+
+ This function acknowledges and causes the deassertion of the SMI activation
+ source.
+
+ @param[in] This The EFI_SMM_CONTROL2_PROTOCOL instance.
+ @param[in] Periodic Optional parameter to repeat at this period
+ one time
+
+ @retval EFI_SUCCESS The SMI/PMI has been engendered.
+ @retval EFI_DEVICE_ERROR The source could not be cleared.
+ @retval EFI_INVALID_PARAMETER The service did not support the Periodic input
+ argument.
+**/
+STATIC
+EFI_STATUS
+EFIAPI
+SmmControl2DxeClear (
+ IN CONST EFI_SMM_CONTROL2_PROTOCOL *This,
+ IN BOOLEAN Periodic OPTIONAL
+ )
+{
+ if (Periodic) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // The PI spec v1.4 explains that Clear() is only supposed to clear software
+ // status; it is not in fact responsible for deasserting the SMI. It gives
+ // two reasons for this: (a) many boards clear the SMI automatically when
+ // entering SMM, (b) if Clear() actually deasserted the SMI, then it could
+ // incorrectly suppress an SMI that was asynchronously asserted between the
+ // last return of the SMI handler and the call made to Clear().
+ //
+ // In fact QEMU automatically deasserts CPU_INTERRUPT_SMI in:
+ // - x86_cpu_exec_interrupt() [target-i386/seg_helper.c], and
+ // - kvm_arch_pre_run() [target-i386/kvm.c].
+ //
+ // So, nothing to do here.
+ //
+ return EFI_SUCCESS;
+}
+
+STATIC EFI_SMM_CONTROL2_PROTOCOL mControl2 = {
+ &SmmControl2DxeTrigger,
+ &SmmControl2DxeClear,
+ MAX_UINTN // MinimumTriggerPeriod -- we don't support periodic SMIs
+};
+
+//
+// Entry point of this driver.
+//
+EFI_STATUS
+EFIAPI
+SmmControl2DxeEntryPoint (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ UINT32 PmBase;
+ UINT32 SmiEnableVal;
+ EFI_STATUS Status;
+
+ //
+ // This module should only be included if SMRAM support is required.
+ //
+ ASSERT (FeaturePcdGet (PcdSmmSmramRequire));
+
+ //
+ // Calculate the absolute IO port address of the SMI Control and Enable
+ // Register. (As noted at the top, the PEI phase has left us with a working
+ // ACPI PM IO space.)
+ //
+ PmBase = PciRead32 (POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE)) &
+ ICH9_PMBASE_MASK;
+ mSmiEnable = PmBase + ICH9_PMBASE_OFS_SMI_EN;
+
+ //
+ // If APMC_EN is pre-set in SMI_EN, that's QEMU's way to tell us that SMI
+ // support is not available. (For example due to KVM lacking it.) Otherwise,
+ // this bit is clear after each reset.
+ //
+ SmiEnableVal = IoRead32 (mSmiEnable);
+ if ((SmiEnableVal & ICH9_SMI_EN_APMC_EN) != 0) {
+ DEBUG ((EFI_D_ERROR, "%a: this Q35 implementation lacks SMI\n",
+ __FUNCTION__));
+ goto FatalError;
+ }
+
+ //
+ // Otherwise, configure the board to inject an SMI when ICH9_APM_CNT is
+ // written to. (See the Trigger() method above.)
+ //
+ SmiEnableVal |= ICH9_SMI_EN_APMC_EN | ICH9_SMI_EN_GBL_SMI_EN;
+ IoWrite32 (mSmiEnable, SmiEnableVal);
+
+ //
+ // Prevent software from undoing the above (until platform reset).
+ //
+ PciOr16 (POWER_MGMT_REGISTER_Q35 (ICH9_GEN_PMCON_1),
+ ICH9_GEN_PMCON_1_SMI_LOCK);
+
+ //
+ // If we can clear GBL_SMI_EN now, that means QEMU's SMI support is not
+ // appropriate.
+ //
+ IoWrite32 (mSmiEnable, SmiEnableVal & ~(UINT32)ICH9_SMI_EN_GBL_SMI_EN);
+ if (IoRead32 (mSmiEnable) != SmiEnableVal) {
+ DEBUG ((EFI_D_ERROR, "%a: failed to lock down GBL_SMI_EN\n",
+ __FUNCTION__));
+ goto FatalError;
+ }
+
+ if (QemuFwCfgS3Enabled ()) {
+ VOID *Registration;
+
+ //
+ // On S3 resume the above register settings have to be repeated. Register a
+ // protocol notify callback that, when boot script saving becomes
+ // available, saves operations equivalent to the above to the boot script.
+ //
+ Status = gBS->CreateEvent (EVT_NOTIFY_SIGNAL, TPL_CALLBACK,
+ OnS3SaveStateInstalled, NULL /* Context */,
+ &mS3SaveStateInstalled);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "%a: CreateEvent: %r\n", __FUNCTION__, Status));
+ goto FatalError;
+ }
+
+ Status = gBS->RegisterProtocolNotify (&gEfiS3SaveStateProtocolGuid,
+ mS3SaveStateInstalled, &Registration);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "%a: RegisterProtocolNotify: %r\n", __FUNCTION__,
+ Status));
+ goto ReleaseEvent;
+ }
+
+ //
+ // Kick the event right now -- maybe the boot script is already saveable.
+ //
+ Status = gBS->SignalEvent (mS3SaveStateInstalled);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "%a: SignalEvent: %r\n", __FUNCTION__, Status));
+ goto ReleaseEvent;
+ }
+ }
+
+ //
+ // We have no pointers to convert to virtual addresses. The handle itself
+ // doesn't matter, as protocol services are not accessible at runtime.
+ //
+ Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
+ &gEfiSmmControl2ProtocolGuid, &mControl2,
+ NULL);
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "%a: InstallMultipleProtocolInterfaces: %r\n",
+ __FUNCTION__, Status));
+ goto ReleaseEvent;
+ }
+
+ return EFI_SUCCESS;
+
+ReleaseEvent:
+ if (mS3SaveStateInstalled != NULL) {
+ gBS->CloseEvent (mS3SaveStateInstalled);
+ }
+
+FatalError:
+ //
+ // We really don't want to continue in this case.
+ //
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ Notification callback for S3SaveState installation.
+
+ @param[in] Event Event whose notification function is being invoked.
+
+ @param[in] Context The pointer to the notification function's context, which
+ is implementation-dependent.
+**/
+STATIC
+VOID
+EFIAPI
+OnS3SaveStateInstalled (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ EFI_S3_SAVE_STATE_PROTOCOL *S3SaveState;
+ UINT32 SmiEnOrMask, SmiEnAndMask;
+ UINT16 GenPmCon1OrMask, GenPmCon1AndMask;
+
+ ASSERT (Event == mS3SaveStateInstalled);
+
+ Status = gBS->LocateProtocol (&gEfiS3SaveStateProtocolGuid,
+ NULL /* Registration */, (VOID **)&S3SaveState);
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ //
+ // These operations were originally done, verified and explained in the entry
+ // point function of the driver.
+ //
+ SmiEnOrMask = ICH9_SMI_EN_APMC_EN | ICH9_SMI_EN_GBL_SMI_EN;
+ SmiEnAndMask = MAX_UINT32;
+ Status = S3SaveState->Write (
+ S3SaveState,
+ EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE,
+ EfiBootScriptWidthUint32,
+ (UINT64)mSmiEnable,
+ &SmiEnOrMask,
+ &SmiEnAndMask
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR, "%a: EFI_BOOT_SCRIPT_IO_READ_WRITE_OPCODE: %r\n",
+ __FUNCTION__, Status));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ GenPmCon1OrMask = ICH9_GEN_PMCON_1_SMI_LOCK;
+ GenPmCon1AndMask = MAX_UINT16;
+ Status = S3SaveState->Write (
+ S3SaveState,
+ EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE,
+ EfiBootScriptWidthUint16,
+ (UINT64)POWER_MGMT_REGISTER_Q35 (ICH9_GEN_PMCON_1),
+ &GenPmCon1OrMask,
+ &GenPmCon1AndMask
+ );
+ if (EFI_ERROR (Status)) {
+ DEBUG ((EFI_D_ERROR,
+ "%a: EFI_BOOT_SCRIPT_PCI_CONFIG_READ_WRITE_OPCODE: %r\n", __FUNCTION__,
+ Status));
+ ASSERT (FALSE);
+ CpuDeadLoop ();
+ }
+
+ DEBUG ((EFI_D_VERBOSE, "%a: boot script fragment saved\n", __FUNCTION__));
+ gBS->CloseEvent (Event);
+ mS3SaveStateInstalled = NULL;
+}
diff --git a/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
new file mode 100644
index 0000000000..0e9f98c287
--- /dev/null
+++ b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
@@ -0,0 +1,66 @@
+## @file
+# A DXE_RUNTIME_DRIVER providing synchronous SMI activations via the
+# EFI_SMM_CONTROL2_PROTOCOL.
+#
+# We expect the PEI phase to have covered the following:
+# - ensure that the underlying QEMU machine type be Q35
+# (responsible: OvmfPkg/SmmAccess/SmmAccessPei.inf)
+# - ensure that the ACPI PM IO space be configured
+# (responsible: OvmfPkg/PlatformPei/PlatformPei.inf)
+#
+# Our own entry point is responsible for confirming the SMI feature and for
+# configuring it.
+#
+# Copyright (C) 2013, 2015, Red Hat, Inc.
+#
+# This program and the accompanying materials are licensed and made available
+# under the terms and conditions of the BSD License which accompanies this
+# distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
+# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = SmmControl2Dxe
+ FILE_GUID = 1206F7CA-A475-4624-A83E-E6FC9BB38E49
+ MODULE_TYPE = DXE_RUNTIME_DRIVER
+ VERSION_STRING = 1.0
+ PI_SPECIFICATION_VERSION = 0x00010400
+ ENTRY_POINT = SmmControl2DxeEntryPoint
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64
+#
+
+[Sources]
+ SmmControl2Dxe.c
+
+[Packages]
+ MdePkg/MdePkg.dec
+ OvmfPkg/OvmfPkg.dec
+
+[LibraryClasses]
+ BaseLib
+ DebugLib
+ IoLib
+ PcdLib
+ PciLib
+ QemuFwCfgLib
+ UefiBootServicesTableLib
+ UefiDriverEntryPoint
+
+[Protocols]
+ gEfiS3SaveStateProtocolGuid ## SOMETIMES_CONSUMES
+ gEfiSmmControl2ProtocolGuid ## PRODUCES
+
+[FeaturePcd]
+ gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
+
+[Depex]
+ TRUE