https://bugzilla.tianocore.org/show_bug.cgi?id=1373 Replace BSD 2-Clause License with BSD+Patent License. This change is based on the following emails: https://lists.01.org/pipermail/edk2-devel/2019-February/036260.html https://lists.01.org/pipermail/edk2-devel/2018-October/030385.html RFCs with detailed process for the license change: V3: https://lists.01.org/pipermail/edk2-devel/2019-March/038116.html V2: https://lists.01.org/pipermail/edk2-devel/2019-March/037669.html V1: https://lists.01.org/pipermail/edk2-devel/2019-March/037500.html Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Michael D Kinney <michael.d.kinney@intel.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com>
		
			
				
	
	
		
			241 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/**@file
 | 
						|
  Negotiate SMI features with QEMU, and configure UefiCpuPkg/PiSmmCpuDxeSmm
 | 
						|
  accordingly.
 | 
						|
 | 
						|
  Copyright (C) 2016-2017, Red Hat, Inc.
 | 
						|
 | 
						|
  SPDX-License-Identifier: BSD-2-Clause-Patent
 | 
						|
**/
 | 
						|
 | 
						|
#include <Library/BaseLib.h>
 | 
						|
#include <Library/DebugLib.h>
 | 
						|
#include <Library/MemoryAllocationLib.h>
 | 
						|
#include <Library/PcdLib.h>
 | 
						|
#include <Library/QemuFwCfgLib.h>
 | 
						|
#include <Library/QemuFwCfgS3Lib.h>
 | 
						|
 | 
						|
#include "SmiFeatures.h"
 | 
						|
 | 
						|
//
 | 
						|
// The following bit value stands for "broadcast SMI" in the
 | 
						|
// "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files.
 | 
						|
//
 | 
						|
#define ICH9_LPC_SMI_F_BROADCAST BIT0
 | 
						|
 | 
						|
//
 | 
						|
// Provides a scratch buffer (allocated in EfiReservedMemoryType type memory)
 | 
						|
// for the S3 boot script fragment to write to and read from.
 | 
						|
//
 | 
						|
#pragma pack (1)
 | 
						|
typedef union {
 | 
						|
  UINT64 Features;
 | 
						|
  UINT8  FeaturesOk;
 | 
						|
} SCRATCH_BUFFER;
 | 
						|
#pragma pack ()
 | 
						|
 | 
						|
//
 | 
						|
// These carry the selector keys of the "etc/smi/requested-features" and
 | 
						|
// "etc/smi/features-ok" fw_cfg files from NegotiateSmiFeatures() to
 | 
						|
// AppendFwCfgBootScript().
 | 
						|
//
 | 
						|
STATIC FIRMWARE_CONFIG_ITEM mRequestedFeaturesItem;
 | 
						|
STATIC FIRMWARE_CONFIG_ITEM mFeaturesOkItem;
 | 
						|
 | 
						|
//
 | 
						|
// Carries the negotiated SMI features from NegotiateSmiFeatures() to
 | 
						|
// AppendFwCfgBootScript().
 | 
						|
//
 | 
						|
STATIC UINT64 mSmiFeatures;
 | 
						|
 | 
						|
/**
 | 
						|
  Negotiate SMI features with QEMU.
 | 
						|
 | 
						|
  @retval FALSE  If SMI feature negotiation is not supported by QEMU. This is
 | 
						|
                 not an error, it just means that SaveSmiFeatures() should not
 | 
						|
                 be called.
 | 
						|
 | 
						|
  @retval TRUE   SMI feature negotiation is supported, and it has completed
 | 
						|
                 successfully as well. (Failure to negotiate is a fatal error
 | 
						|
                 and the function never returns in that case.)
 | 
						|
**/
 | 
						|
BOOLEAN
 | 
						|
NegotiateSmiFeatures (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  FIRMWARE_CONFIG_ITEM SupportedFeaturesItem;
 | 
						|
  UINTN                SupportedFeaturesSize;
 | 
						|
  UINTN                RequestedFeaturesSize;
 | 
						|
  UINTN                FeaturesOkSize;
 | 
						|
 | 
						|
  //
 | 
						|
  // Look up the fw_cfg files used for feature negotiation. The selector keys
 | 
						|
  // of "etc/smi/requested-features" and "etc/smi/features-ok" are saved
 | 
						|
  // statically. If the files are missing, then QEMU doesn't support SMI
 | 
						|
  // feature negotiation.
 | 
						|
  //
 | 
						|
  if (RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/supported-features",
 | 
						|
                      &SupportedFeaturesItem, &SupportedFeaturesSize)) ||
 | 
						|
      RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/requested-features",
 | 
						|
                      &mRequestedFeaturesItem, &RequestedFeaturesSize)) ||
 | 
						|
      RETURN_ERROR (QemuFwCfgFindFile ("etc/smi/features-ok",
 | 
						|
                      &mFeaturesOkItem, &FeaturesOkSize))) {
 | 
						|
    DEBUG ((DEBUG_INFO, "%a: SMI feature negotiation unavailable\n",
 | 
						|
      __FUNCTION__));
 | 
						|
    return FALSE;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If the files are present but their sizes disagree with us, that's a fatal
 | 
						|
  // error (we can't trust the behavior of SMIs either way).
 | 
						|
  //
 | 
						|
  if (SupportedFeaturesSize != sizeof mSmiFeatures ||
 | 
						|
      RequestedFeaturesSize != sizeof mSmiFeatures ||
 | 
						|
      FeaturesOkSize != sizeof (UINT8)) {
 | 
						|
    DEBUG ((DEBUG_ERROR, "%a: size mismatch in feature negotiation\n",
 | 
						|
      __FUNCTION__));
 | 
						|
    goto FatalError;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Get the features supported by the host.
 | 
						|
  //
 | 
						|
  QemuFwCfgSelectItem (SupportedFeaturesItem);
 | 
						|
  QemuFwCfgReadBytes (sizeof mSmiFeatures, &mSmiFeatures);
 | 
						|
 | 
						|
  //
 | 
						|
  // We want broadcast SMI and nothing else.
 | 
						|
  //
 | 
						|
  mSmiFeatures &= ICH9_LPC_SMI_F_BROADCAST;
 | 
						|
  QemuFwCfgSelectItem (mRequestedFeaturesItem);
 | 
						|
  QemuFwCfgWriteBytes (sizeof mSmiFeatures, &mSmiFeatures);
 | 
						|
 | 
						|
  //
 | 
						|
  // Invoke feature validation in QEMU. If the selection is accepted, the
 | 
						|
  // features will be locked down. If the selection is rejected, feature
 | 
						|
  // negotiation remains open; however we don't know what to do in that case,
 | 
						|
  // so that's a fatal error.
 | 
						|
  //
 | 
						|
  QemuFwCfgSelectItem (mFeaturesOkItem);
 | 
						|
  if (QemuFwCfgRead8 () != 1) {
 | 
						|
    DEBUG ((DEBUG_ERROR, "%a: negotiation failed for feature bitmap 0x%Lx\n",
 | 
						|
      __FUNCTION__, mSmiFeatures));
 | 
						|
    goto FatalError;
 | 
						|
  }
 | 
						|
 | 
						|
  if ((mSmiFeatures & ICH9_LPC_SMI_F_BROADCAST) == 0) {
 | 
						|
    //
 | 
						|
    // If we can't get broadcast SMIs from QEMU, that's acceptable too,
 | 
						|
    // although not optimal.
 | 
						|
    //
 | 
						|
    DEBUG ((DEBUG_INFO, "%a: SMI broadcast unavailable\n", __FUNCTION__));
 | 
						|
  } else {
 | 
						|
    //
 | 
						|
    // Configure the traditional AP sync / SMI delivery mode for
 | 
						|
    // PiSmmCpuDxeSmm. Effectively, restore the UefiCpuPkg defaults, from which
 | 
						|
    // the original QEMU behavior (i.e., unicast SMI) used to differ.
 | 
						|
    //
 | 
						|
    if (RETURN_ERROR (PcdSet64S (PcdCpuSmmApSyncTimeout, 1000000)) ||
 | 
						|
        RETURN_ERROR (PcdSet8S (PcdCpuSmmSyncMode, 0x00))) {
 | 
						|
      DEBUG ((DEBUG_ERROR, "%a: PiSmmCpuDxeSmm PCD configuration failed\n",
 | 
						|
        __FUNCTION__));
 | 
						|
      goto FatalError;
 | 
						|
    }
 | 
						|
    DEBUG ((DEBUG_INFO, "%a: using SMI broadcast\n", __FUNCTION__));
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Negotiation successful (although we may not have gotten the optimal
 | 
						|
  // feature set).
 | 
						|
  //
 | 
						|
  return TRUE;
 | 
						|
 | 
						|
FatalError:
 | 
						|
  ASSERT (FALSE);
 | 
						|
  CpuDeadLoop ();
 | 
						|
  //
 | 
						|
  // Keep the compiler happy.
 | 
						|
  //
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
  FW_CFG_BOOT_SCRIPT_CALLBACK_FUNCTION provided to QemuFwCfgS3Lib.
 | 
						|
**/
 | 
						|
STATIC
 | 
						|
VOID
 | 
						|
EFIAPI
 | 
						|
AppendFwCfgBootScript (
 | 
						|
  IN OUT VOID *Context,              OPTIONAL
 | 
						|
  IN OUT VOID *ExternalScratchBuffer
 | 
						|
  )
 | 
						|
{
 | 
						|
  SCRATCH_BUFFER *ScratchBuffer;
 | 
						|
  RETURN_STATUS  Status;
 | 
						|
 | 
						|
  ScratchBuffer = ExternalScratchBuffer;
 | 
						|
 | 
						|
  //
 | 
						|
  // Write the negotiated feature bitmap into "etc/smi/requested-features".
 | 
						|
  //
 | 
						|
  ScratchBuffer->Features = mSmiFeatures;
 | 
						|
  Status = QemuFwCfgS3ScriptWriteBytes (mRequestedFeaturesItem,
 | 
						|
             sizeof ScratchBuffer->Features);
 | 
						|
  if (RETURN_ERROR (Status)) {
 | 
						|
    goto FatalError;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // Read back "etc/smi/features-ok". This invokes the feature validation &
 | 
						|
  // lockdown. (The validation succeeded at first boot.)
 | 
						|
  //
 | 
						|
  Status = QemuFwCfgS3ScriptReadBytes (mFeaturesOkItem,
 | 
						|
             sizeof ScratchBuffer->FeaturesOk);
 | 
						|
  if (RETURN_ERROR (Status)) {
 | 
						|
    goto FatalError;
 | 
						|
  }
 | 
						|
 | 
						|
  //
 | 
						|
  // If "etc/smi/features-ok" read as 1, we're good. Otherwise, hang the S3
 | 
						|
  // resume process.
 | 
						|
  //
 | 
						|
  Status = QemuFwCfgS3ScriptCheckValue (&ScratchBuffer->FeaturesOk,
 | 
						|
             sizeof ScratchBuffer->FeaturesOk, MAX_UINT8, 1);
 | 
						|
  if (RETURN_ERROR (Status)) {
 | 
						|
    goto FatalError;
 | 
						|
  }
 | 
						|
 | 
						|
  DEBUG ((DEBUG_VERBOSE, "%a: SMI feature negotiation boot script saved\n",
 | 
						|
    __FUNCTION__));
 | 
						|
  return;
 | 
						|
 | 
						|
FatalError:
 | 
						|
  ASSERT (FALSE);
 | 
						|
  CpuDeadLoop ();
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/**
 | 
						|
  Append a boot script fragment that will re-select the previously negotiated
 | 
						|
  SMI features during S3 resume.
 | 
						|
**/
 | 
						|
VOID
 | 
						|
SaveSmiFeatures (
 | 
						|
  VOID
 | 
						|
  )
 | 
						|
{
 | 
						|
  RETURN_STATUS Status;
 | 
						|
 | 
						|
  //
 | 
						|
  // We are already running at TPL_CALLBACK, on the stack of
 | 
						|
  // OnS3SaveStateInstalled(). But that's okay, we can easily queue more
 | 
						|
  // notification functions while executing a notification function.
 | 
						|
  //
 | 
						|
  Status = QemuFwCfgS3CallWhenBootScriptReady (AppendFwCfgBootScript, NULL,
 | 
						|
             sizeof (SCRATCH_BUFFER));
 | 
						|
  if (RETURN_ERROR (Status)) {
 | 
						|
    ASSERT (FALSE);
 | 
						|
    CpuDeadLoop ();
 | 
						|
  }
 | 
						|
}
 |