The EFI_SMM_COMMUNICATION_PROTOCOL implementation that is provided by the
SMM core depends on EFI_SMM_CONTROL2_PROTOCOL; see the
mSmmControl2->Trigger() call in the SmmCommunicationCommunicate() function
[MdeModulePkg/Core/PiSmmCore/PiSmmIpl.c].

Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <ler...@redhat.com>
---

Notes:
    v2:
    - Set (APMC_EN|GBL_SMI_EN) in SMI_EN from the boot script at S3 resume.
      Otherwise, because SMI_EN is cleared during warm reset,
      SmmControl2DxeTrigger() would fail to trigger an SMI, and variable
      access through the runtime services would fail.
    
      Set SMI_LOCK in GEN_PMCON_1 similarly.

 OvmfPkg/OvmfPkgIa32.dsc                   |   1 +
 OvmfPkg/OvmfPkgIa32X64.dsc                |   1 +
 OvmfPkg/OvmfPkgX64.dsc                    |   1 +
 OvmfPkg/OvmfPkgIa32.fdf                   |   1 +
 OvmfPkg/OvmfPkgIa32X64.fdf                |   1 +
 OvmfPkg/OvmfPkgX64.fdf                    |   1 +
 OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf |  65 ++++
 OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c   | 365 ++++++++++++++++++++
 8 files changed, 436 insertions(+)

diff --git a/OvmfPkg/OvmfPkgIa32.dsc b/OvmfPkg/OvmfPkgIa32.dsc
index de260e4..7872b6b 100644
--- a/OvmfPkg/OvmfPkgIa32.dsc
+++ b/OvmfPkg/OvmfPkgIa32.dsc
@@ -674,4 +674,5 @@ [Components]
 
 !if $(SMM_REQUIRE) == TRUE
   OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+  OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
 !endif
diff --git a/OvmfPkg/OvmfPkgIa32X64.dsc b/OvmfPkg/OvmfPkgIa32X64.dsc
index 8e82613..081fd84 100644
--- a/OvmfPkg/OvmfPkgIa32X64.dsc
+++ b/OvmfPkg/OvmfPkgIa32X64.dsc
@@ -681,4 +681,5 @@ [Components.X64]
 
 !if $(SMM_REQUIRE) == TRUE
   OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+  OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
 !endif
diff --git a/OvmfPkg/OvmfPkgX64.dsc b/OvmfPkg/OvmfPkgX64.dsc
index 4df7de1..6f42869 100644
--- a/OvmfPkg/OvmfPkgX64.dsc
+++ b/OvmfPkg/OvmfPkgX64.dsc
@@ -679,4 +679,5 @@ [Components]
 
 !if $(SMM_REQUIRE) == TRUE
   OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+  OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
 !endif
diff --git a/OvmfPkg/OvmfPkgIa32.fdf b/OvmfPkg/OvmfPkgIa32.fdf
index 285720f..43c9c30 100644
--- a/OvmfPkg/OvmfPkgIa32.fdf
+++ b/OvmfPkg/OvmfPkgIa32.fdf
@@ -357,6 +357,7 @@ [FV.DXEFV]
 
 !if $(SMM_REQUIRE) == TRUE
 INF  OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+INF  OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
 !endif
 
 
################################################################################
diff --git a/OvmfPkg/OvmfPkgIa32X64.fdf b/OvmfPkg/OvmfPkgIa32X64.fdf
index 02e8752..9446896 100644
--- a/OvmfPkg/OvmfPkgIa32X64.fdf
+++ b/OvmfPkg/OvmfPkgIa32X64.fdf
@@ -357,6 +357,7 @@ [FV.DXEFV]
 
 !if $(SMM_REQUIRE) == TRUE
 INF  OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+INF  OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
 !endif
 
 
################################################################################
diff --git a/OvmfPkg/OvmfPkgX64.fdf b/OvmfPkg/OvmfPkgX64.fdf
index f04c36b..b272b76 100644
--- a/OvmfPkg/OvmfPkgX64.fdf
+++ b/OvmfPkg/OvmfPkgX64.fdf
@@ -357,6 +357,7 @@ [FV.DXEFV]
 
 !if $(SMM_REQUIRE) == TRUE
 INF  OvmfPkg/SmmAccess/SmmAccess2Dxe.inf
+INF  OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
 !endif
 
 
################################################################################
diff --git a/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf 
b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
new file mode 100644
index 0000000..bc66a27
--- /dev/null
+++ b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
@@ -0,0 +1,65 @@
+## @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]
+  DebugLib
+  IoLib
+  PcdLib
+  PciLib
+  QemuFwCfgLib
+  UefiBootServicesTableLib
+  UefiDriverEntryPoint
+
+[Protocols]
+  gEfiS3SaveStateProtocolGuid   # PROTOCOL SOMETIMES_CONSUMED
+  gEfiSmmControl2ProtocolGuid   # PROTOCOL ALWAYS_PRODUCED
+
+[FeaturePcd]
+  gUefiOvmfPkgTokenSpaceGuid.PcdSmmSmramRequire
+
+[Depex]
+  TRUE
diff --git a/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c 
b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c
new file mode 100644
index 0000000..e895fd6
--- /dev/null
+++ b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c
@@ -0,0 +1,365 @@
+/** @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/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>
+
+//
+// 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
+};
+
+//
+// Forward declaration.
+//
+STATIC
+VOID
+EFIAPI
+OnS3SaveStateInstalled (
+  IN EFI_EVENT Event,
+  IN VOID      *Context
+  );
+
+//
+// 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() is never compiled out, and it respects PcdDebugPropertyMask (ie.
+  // prevent further execution with CPU breakpoint vs. dead loop).
+  //
+  _ASSERT (FALSE);
+  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);
+  }
+
+  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);
+  }
+
+  DEBUG ((EFI_D_VERBOSE, "%a: boot script fragment saved\n", __FUNCTION__));
+  gBS->CloseEvent (Event);
+  mS3SaveStateInstalled = NULL;
+}
-- 
1.8.3.1


_______________________________________________
edk2-devel mailing list
edk2-devel@lists.01.org
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to