/**@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 #include #include #include #include #include #include #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 // // The following bit value stands for "enable CPU hotplug, and inject an SMI // with control value ICH9_APM_CNT_CPU_HOTPLUG upon hotplug", in the // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files. // #define ICH9_LPC_SMI_F_CPU_HOTPLUG BIT1 // // The following bit value stands for "enable CPU hot-unplug, and inject an SMI // with control value ICH9_APM_CNT_CPU_HOTPLUG upon hot-unplug", in the // "etc/smi/supported-features" and "etc/smi/requested-features" fw_cfg files. // #define ICH9_LPC_SMI_F_CPU_HOT_UNPLUG BIT2 // // 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; UINT64 RequestedFeaturesMask; // // 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", __func__ )); 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", __func__ )); goto FatalError; } // // Get the features supported by the host. // QemuFwCfgSelectItem (SupportedFeaturesItem); QemuFwCfgReadBytes (sizeof mSmiFeatures, &mSmiFeatures); // // We want broadcast SMI, SMI on CPU hotplug, SMI on CPU hot-unplug // and nothing else. // RequestedFeaturesMask = ICH9_LPC_SMI_F_BROADCAST; if (!MemEncryptSevIsEnabled ()) { // // For now, we only support hotplug with SEV disabled. // RequestedFeaturesMask |= ICH9_LPC_SMI_F_CPU_HOTPLUG; RequestedFeaturesMask |= ICH9_LPC_SMI_F_CPU_HOT_UNPLUG; } mSmiFeatures &= RequestedFeaturesMask; 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", __func__, 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", __func__)); } 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", __func__ )); goto FatalError; } DEBUG ((DEBUG_INFO, "%a: using SMI broadcast\n", __func__)); } if ((mSmiFeatures & ICH9_LPC_SMI_F_CPU_HOTPLUG) == 0) { DEBUG ((DEBUG_INFO, "%a: CPU hotplug not negotiated\n", __func__)); } else { DEBUG (( DEBUG_INFO, "%a: CPU hotplug with SMI negotiated\n", __func__ )); } if ((mSmiFeatures & ICH9_LPC_SMI_F_CPU_HOT_UNPLUG) == 0) { DEBUG ((DEBUG_INFO, "%a: CPU hot-unplug not negotiated\n", __func__)); } else { DEBUG (( DEBUG_INFO, "%a: CPU hot-unplug with SMI negotiated\n", __func__ )); } // // 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", __func__ )); 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 (); } }