OvmfPkg/PlatformPei: program MSR_IA32_FEATURE_CONTROL from fw_cfg

Under certain circumstances, QEMU exposes the "etc/msr_feature_control"
fw_cfg file, with a 64-bit little endian value. The firmware is supposed
to write this value to MSR_IA32_FEATURE_CONTROL (0x3a), on all processors,
on the normal and the S3 resume boot paths.

Utilize EFI_PEI_MPSERVICES_PPI to implement this feature.

Cc: Jeff Fan <jeff.fan@intel.com>
Cc: Jordan Justen <jordan.l.justen@intel.com>
Cc: Michael Kinney <michael.d.kinney@intel.com>
Fixes: https://github.com/tianocore/edk2/issues/97
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Jeff Fan <jeff.fan@intel.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
diff --git a/OvmfPkg/PlatformPei/FeatureControl.c b/OvmfPkg/PlatformPei/FeatureControl.c
new file mode 100644
index 0000000..b91d988
--- /dev/null
+++ b/OvmfPkg/PlatformPei/FeatureControl.c
@@ -0,0 +1,134 @@
+/**@file

+  Install a callback when necessary for setting the Feature Control MSR on all

+  processors.

+

+  Copyright (C) 2016, 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.

+**/

+

+#include <Library/DebugLib.h>

+#include <Library/PeiServicesLib.h>

+#include <Library/QemuFwCfgLib.h>

+#include <Ppi/MpServices.h>

+#include <Register/Msr/Core2Msr.h>

+

+#include "Platform.h"

+

+//

+// The value to be written to the Feature Control MSR, retrieved from fw_cfg.

+//

+STATIC UINT64 mFeatureControlValue;

+

+/**

+  Write the Feature Control MSR on an Application Processor or the Boot

+  Processor.

+

+  All APs execute this function in parallel. The BSP executes the function

+  separately.

+

+  @param[in,out] WorkSpace  Pointer to the input/output argument workspace

+                            shared by all processors.

+**/

+STATIC

+VOID

+EFIAPI

+WriteFeatureControl (

+  IN OUT VOID *WorkSpace

+  )

+{

+  AsmWriteMsr64 (MSR_CORE2_FEATURE_CONTROL, mFeatureControlValue);

+}

+

+/**

+  Notification function called when EFI_PEI_MP_SERVICES_PPI becomes available.

+

+  @param[in] PeiServices      Indirect reference to the PEI Services Table.

+  @param[in] NotifyDescriptor Address of the notification descriptor data

+                              structure.

+  @param[in] Ppi              Address of the PPI that was installed.

+

+  @return  Status of the notification. The status code returned from this

+           function is ignored.

+**/

+STATIC

+EFI_STATUS

+EFIAPI

+OnMpServicesAvailable (

+  IN EFI_PEI_SERVICES           **PeiServices,

+  IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,

+  IN VOID                       *Ppi

+  )

+{

+  EFI_PEI_MP_SERVICES_PPI *MpServices;

+  EFI_STATUS              Status;

+

+  DEBUG ((EFI_D_VERBOSE, "%a: %a\n", gEfiCallerBaseName, __FUNCTION__));

+

+  //

+  // Write the MSR on all the APs in parallel.

+  //

+  MpServices = Ppi;

+  Status = MpServices->StartupAllAPs (

+                         (CONST EFI_PEI_SERVICES **)PeiServices,

+                         MpServices,

+                         WriteFeatureControl, // Procedure

+                         FALSE,               // SingleThread

+                         0,                   // TimeoutInMicroSeconds: inf.

+                         NULL                 // ProcedureArgument

+                         );

+  if (EFI_ERROR (Status) && Status != EFI_NOT_STARTED) {

+    DEBUG ((EFI_D_ERROR, "%a: StartupAllAps(): %r\n", __FUNCTION__, Status));

+    return Status;

+  }

+

+  //

+  // Now write the MSR on the BSP too.

+  //

+  WriteFeatureControl (NULL);

+  return EFI_SUCCESS;

+}

+

+//

+// Notification object for registering the callback, for when

+// EFI_PEI_MP_SERVICES_PPI becomes available.

+//

+STATIC CONST EFI_PEI_NOTIFY_DESCRIPTOR mMpServicesNotify = {

+  EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | // Flags

+  EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST,

+  &gEfiPeiMpServicesPpiGuid,               // Guid

+  OnMpServicesAvailable                    // Notify

+};

+

+VOID

+InstallFeatureControlCallback (

+  VOID

+  )

+{

+  EFI_STATUS           Status;

+  FIRMWARE_CONFIG_ITEM FwCfgItem;

+  UINTN                FwCfgSize;

+

+  Status = QemuFwCfgFindFile ("etc/msr_feature_control", &FwCfgItem,

+             &FwCfgSize);

+  if (EFI_ERROR (Status) || FwCfgSize != sizeof mFeatureControlValue) {

+    //

+    // Nothing to do.

+    //

+    return;

+  }

+  QemuFwCfgSelectItem (FwCfgItem);

+  QemuFwCfgReadBytes (sizeof mFeatureControlValue, &mFeatureControlValue);

+

+  Status = PeiServicesNotifyPpi (&mMpServicesNotify);

+  if (EFI_ERROR (Status)) {

+    DEBUG ((EFI_D_ERROR, "%a: failed to set up MP Services callback: %r\n",

+      __FUNCTION__, Status));

+  }

+}

diff --git a/OvmfPkg/PlatformPei/Platform.c b/OvmfPkg/PlatformPei/Platform.c
index 75f7480..ca1e6dc 100644
--- a/OvmfPkg/PlatformPei/Platform.c
+++ b/OvmfPkg/PlatformPei/Platform.c
@@ -612,6 +612,7 @@
   }

 

   MiscInitialization ();

+  InstallFeatureControlCallback ();

 

   return EFI_SUCCESS;

 }

diff --git a/OvmfPkg/PlatformPei/Platform.h b/OvmfPkg/PlatformPei/Platform.h
index bb988ea..eda765b 100644
--- a/OvmfPkg/PlatformPei/Platform.h
+++ b/OvmfPkg/PlatformPei/Platform.h
@@ -73,6 +73,11 @@
   VOID

   );

 

+VOID

+InstallFeatureControlCallback (

+  VOID

+  );

+

 EFI_STATUS

 InitializeXen (

   VOID

diff --git a/OvmfPkg/PlatformPei/PlatformPei.inf b/OvmfPkg/PlatformPei/PlatformPei.inf
index 5d765ba..776a4ab 100644
--- a/OvmfPkg/PlatformPei/PlatformPei.inf
+++ b/OvmfPkg/PlatformPei/PlatformPei.inf
@@ -30,6 +30,7 @@
 

 [Sources]

   Cmos.c

+  FeatureControl.c

   Fv.c

   MemDetect.c

   Platform.c

@@ -104,6 +105,7 @@
 

 [Ppis]

   gEfiPeiMasterBootModePpiGuid

+  gEfiPeiMpServicesPpiGuid

 

 [Depex]

   TRUE