On 09/10/2015 23:58, Laszlo Ersek wrote:
> 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;
> +}

I tested multiprocessor SMM with your branch, and we're really close.  KVM
doesn't handle SMIs correctly (ironically I fixed the same bug in QEMU with
commit a9bad65, "target-i386: wake up processors that receive an SMI",
2015-05-19).  TCG boots... but it takes about 5 minutes.

This is because the SMI handler, and in particular SmmWaitForApArrival,
expects that SmmControl2DxeTrigger triggers an SMI IPI on all processors
rather than just the BSP.  QEMU's port 0xb2 only affects the current CPU,
thus all SMM invocations loop for a second (the default value of
PcdCpuSmmApSyncTimeout) before SmmWaitForApArrival sends another SMI IPI
to the APs.

This could also be fixed by setting gUefiCpuPkgTokenSpaceGuid.PcdCpuSmmSyncMode
to 1 (aka SmmCpuSyncModeRelaxedAp), but this only takes effect on X64 CPUs
which also ironically we don't support yet.  Therefore, the following seems to
be the correct fix.  With it, TCG still takes a while to boot, but KVM gets
to the UEFI shell very fast.

[[email protected]: Use LocalApicLib to send trigger an SMI on all
 processors]
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Paolo Bonzini <[email protected]>

diff --git a/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c 
b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c
index e895fd6..17fbb88 100644
--- a/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c
+++ b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.c
@@ -28,6 +28,7 @@
 #include <IndustryStandard/Q35MchIch9.h>
 #include <Library/DebugLib.h>
 #include <Library/IoLib.h>
+#include <Library/LocalApicLib.h>
 #include <Library/PcdLib.h>
 #include <Library/PciLib.h>
 #include <Library/QemuFwCfgLib.h>
@@ -88,6 +89,15 @@ SmmControl2DxeTrigger (
   }
 
   //
+  // The write to the control register is synchronous and only affects the
+  // current CPU, so bring in the APs first.  The SMI handler expects that
+  // all APs will rendez-vous within one PcdCpuSmmApSyncTimeout (though it
+  // helpfully tries sending SMI IPIs to the missing processors if there are
+  // any).
+  //
+  SendSmiIpiAllExcludingSelf ();
+
+  //
   // 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
diff --git a/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf 
b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
index bc66a27..f9c4821 100644
--- a/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
+++ b/OvmfPkg/SmmControl2Dxe/SmmControl2Dxe.inf
@@ -44,10 +44,12 @@
 [Packages]
   MdePkg/MdePkg.dec
   OvmfPkg/OvmfPkg.dec
+  UefiCpuPkg/UefiCpuPkg.dec
 
 [LibraryClasses]
   DebugLib
   IoLib
+  LocalApicLib
   PcdLib
   PciLib
   QemuFwCfgLib

_______________________________________________
edk2-devel mailing list
[email protected]
https://lists.01.org/mailman/listinfo/edk2-devel

Reply via email to