/** @file The CPU specific programming for PiSmmCpuDxeSmm module, such as SMRR, IED. @copyright INTEL CONFIDENTIAL Copyright 2015 - 2021 Intel Corporation. The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material may contain trade secrets and proprietary and confidential information of Intel Corporation and its suppliers and licensors, and is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel's prior express written permission. No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing. Unless otherwise agreed by Intel in writing, you may not remove or alter this notice or any other notice embedded in Materials by Intel or Intel's suppliers or licensors in any way. This file contains an 'Intel Peripheral Driver' and is uniquely identified as "Intel Reference Module" and is licensed for Intel CPUs and chipsets under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to additional terms of the license agreement. @par Specification Reference: **/ #include "SmmCpuFeatures.h" #include //////// // Below section is common definition //////// // // Assumes UP, or MP with identical feature set // CPU_SMM_FEATURE_CONTEXT mFeatureContext; BOOLEAN mSmmMsrSaveStateEnable = FALSE; BOOLEAN mSaveStateInMsr = FALSE; BOOLEAN mSmmInitSaveStateInMsr; BOOLEAN mSmrrLockSupported; UINT32 mSmrr2Base; UINT32 mSmrr2Size; UINT32 mSmmBaseNotCoveredBySmrr; UINT32 mSmmSizeNotCoveredBySmrr = 0; UINT8 mSmrr2CacheType; BOOLEAN mSmmUseDelayIndication; BOOLEAN mSmmUseBlockIndication; BOOLEAN mSmmEnableIndication; BOOLEAN mSmmSpsPpam11Enable = FALSE; BOOLEAN mSmmSpsStateSaveEnable = FALSE; UINT32 mSmmTestRsvMemorySize; BOOLEAN mSmmProcTraceEnable; EFI_PROCESSOR_INFORMATION *mSmmProcessorInfo; UINT32 *mSmBase; SMM_CPU_SYNC_FEATURE *mSmmSyncFeature; // // Protected Mode Entrypoint // BOOLEAN mSmmProtectedModeEnable; SMM_PROT_MODE_CONTEXT *mSmmProtModeContext; // // Set default value to assume MSR_SMM_FEATURE_CONTROL is not supported // BOOLEAN mSmmFeatureControlSupported; BOOLEAN mSmmSupovrStateLock = FALSE; EFI_SMRAM_DESCRIPTOR *mSmmFeatureSmramRanges; UINTN mSmmFeatureSmramRangeCount; extern IA32_DESCRIPTOR gSmmFeatureSmiHandlerIdtr; // // Variables from Protected Mode SMI Entry Template // extern volatile UINT32 gSmmFeatureSmiStack; extern UINT32 gSmmFeatureSmbase; extern UINT32 gSmmStackSize; extern UINT32 gSmmFeatureSmiCr3; extern BOOLEAN gSmmFeatureXdSupported; extern UINT32 gProtModeSmbase; extern volatile UINT8 gcSmmFeatureSmiHandlerTemplate[]; extern CONST UINT16 gcSmmFeatureSmiHandlerSize; extern UINT32 gProtModeIdtr; extern UINT32 gPMStackDesc[2]; extern SPIN_LOCK *mInternalDebugLock; extern BOOLEAN gSmmFeatureRing3Supported; extern BOOLEAN gSpaEnable; extern IA32_PROT_DESCRIPTOR gGdtDesc; /** Allocate pages for code. @param Pages Number of pages to be allocated. @return Allocated memory. **/ VOID * SmmFeatureAllocateCodePages ( IN UINTN Pages ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS Memory; if (Pages == 0) { return NULL; } Status = gSmst->SmmAllocatePages (AllocateAnyPages, EfiRuntimeServicesCode, Pages, &Memory); if (EFI_ERROR (Status)) { return NULL; } return (VOID *) (UINTN) Memory; } /** Find out SMRAM range information from SMM ACCESS2 Protocol. **/ VOID FindSmramRange ( VOID ) { EFI_STATUS Status; UINTN Size; EFI_SMM_ACCESS2_PROTOCOL *SmmAccess; // // Get SMM Access Protocol // Status = gBS->LocateProtocol (&gEfiSmmAccess2ProtocolGuid, NULL, (VOID **)&SmmAccess); ASSERT_EFI_ERROR (Status); // // Get SMRAM information // Size = 0; Status = SmmAccess->GetCapabilities (SmmAccess, &Size, NULL); ASSERT (Status == EFI_BUFFER_TOO_SMALL); mSmmFeatureSmramRanges = (EFI_SMRAM_DESCRIPTOR *)AllocatePool (Size); ASSERT (mSmmFeatureSmramRanges != NULL); Status = SmmAccess->GetCapabilities (SmmAccess, &Size, mSmmFeatureSmramRanges); ASSERT_EFI_ERROR (Status); mSmmFeatureSmramRangeCount = Size / sizeof (EFI_SMRAM_DESCRIPTOR); } /** The constructor function @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. @retval EFI_OUT_OF_RESOURCES Do not have enough resources to complete the function. **/ EFI_STATUS EFIAPI SmmCpuFeaturesLibConstructor ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { UINT32 RegEax; UINT32 RegEdx; UINTN FamilyId; UINTN ModelId; UINTN SpinLockSize; VOID *SpinLockBuffer; #if FixedPcdGetBool (PcdPpamEnable) == 1 UINT8 NrPpamSupport; #endif DXE_CPU_POLICY_PROTOCOL *CpuPolicyData; EFI_STATUS Status; // // Initialize address fixup // SmmCpuFeaturesLibSmiEntryFixupAddress (); // // Retrieve CPU Family and Model // AsmCpuid(CPUID_VERSION_INFO, &RegEax, NULL, NULL, NULL); DEBUG ((EFI_D_INFO, "CPUID - 0x%08x\n", RegEax)); FamilyId = (RegEax >> 8) & 0xf; ModelId = (RegEax >> 4) & 0xf; if (FamilyId == 0x06 || FamilyId == 0x0f) { ModelId = ModelId | ((RegEax >> 12) & 0xf0); } DEBUG ((EFI_D_INFO, "FamilyId - 0x%08x\n", FamilyId)); DEBUG ((EFI_D_INFO, "ModelId - 0x%08x\n", ModelId)); RegEdx = 0; AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); if (RegEax >= CPUID_EXTENDED_CPU_SIG) { AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); } // // Determine the mode of the CPU at the time an SMI occurs // Intel(R) 64 and IA-32 Architectures Software Developer's Manual // Volume 3C, Section 34.4.1.1 // mSmmFeatureSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT; if ((RegEdx & BIT29) != 0) { mSmmFeatureSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT; } if (FamilyId == 0x06) { if (ModelId == 0x17 || ModelId == 0x0f || ModelId == 0x1c) { mSmmFeatureSaveStateRegisterLma = EFI_SMM_SAVE_STATE_REGISTER_LMA_64BIT; } } gSmmFeatureXdSupported = FALSE; if ((RegEdx & CPUID1_EDX_XD_SUPPORT) != 0) { gSmmFeatureXdSupported = TRUE; } // // Allocate page for SPIN LOCK buffer // SpinLockSize = GetSpinLockProperties (); SpinLockBuffer = AllocatePages (EFI_SIZE_TO_PAGES (SpinLockSize * 3)); if (SpinLockBuffer == NULL) { ASSERT (SpinLockBuffer != NULL); return EFI_OUT_OF_RESOURCES; } mMsrSpinLock = (SPIN_LOCK *)SpinLockBuffer; mInternalDebugLock = (SPIN_LOCK *) ((UINTN) SpinLockBuffer + SpinLockSize * 2); InitializeSpinLock (mInternalDebugLock); // // Initialize spin lock for MSR access // InitializeSpinLock (mMsrSpinLock); mSmBase = (UINT32 *)AllocatePool(sizeof(UINT32) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); ASSERT(mSmBase != NULL); mSmmSyncFeature = (SMM_CPU_SYNC_FEATURE *)AllocatePool (sizeof (SMM_CPU_SYNC_FEATURE) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber)); ASSERT (mSmmSyncFeature != NULL); // // Related PCD values are retrieved into global variables. // mSmmProtectedModeEnable = PcdGetBool (PcdCpuSmmProtectedModeEnable); // // Update module variable according to policy setting. // Status = gBS->LocateProtocol (&gDxeCpuPolicyProtocolGuid, NULL, (VOID **) &CpuPolicyData); ASSERT_EFI_ERROR (Status); if (!EFI_ERROR (Status)) { mSmmMsrSaveStateEnable = CpuPolicyData->SmmMsrSaveStateEnable; mSmmUseDelayIndication = CpuPolicyData->SmmUseDelayIndication; mSmmUseBlockIndication = CpuPolicyData->SmmUseBlockIndication; mSmmEnableIndication = CpuPolicyData->SmmUseSmmEnableIndication; mSmmProcTraceEnable = CpuPolicyData->SmmProcTraceEnable; #if FixedPcdGetBool (PcdPpamEnable) == 1 NrPpamSupport = NO_NR_PPAM_SUPPORT; if (CpuPolicyData->DgrEnable) { NrPpamSupport = NiftyRockSupportLevel (); if (NrPpamSupport == NR_PPAM_11_SUPPORT) { DEBUG ((DEBUG_INFO, "DGR with Nifty Rock 1.1 feature support is enabled\n")); mSmmSpsPpam11Enable = TRUE; #if FixedPcdGetBool (PcdSpsStateSaveEnable) == 1 if (CpuPolicyData->DgrStateSaveProtect) { mSmmSpsStateSaveEnable = TRUE; } #endif } else { DEBUG ((DEBUG_INFO, "DGR feature either NOT support by CPU or disabled in Policy.\n")); } } #endif } // // Dump some SMM feature values // DEBUG ((EFI_D_INFO, "SmmMsrSaveStateEnable = %d\n", mSmmMsrSaveStateEnable)); DEBUG ((EFI_D_INFO, "SmmUseSmmEnableIndication = %d\n", mSmmEnableIndication)); DEBUG ((EFI_D_INFO, "SmmUseBlockIndication = %d\n", mSmmUseBlockIndication)); DEBUG ((EFI_D_INFO, "SmmUseDelayIndication = %d\n", mSmmUseDelayIndication)); DEBUG ((EFI_D_INFO, "PcdCpuSmmProtectedModeEnable = %d\n", mSmmProtectedModeEnable)); if (mSmmProtectedModeEnable) { mSmmProtModeContext = SmmFeatureAllocateCodePages (EFI_SIZE_TO_PAGES(sizeof(SMM_PROT_MODE_CONTEXT) * PcdGet32(PcdCpuMaxLogicalProcessorNumber))); ASSERT(mSmmProtModeContext != NULL); } mSmrr2Base = PcdGet32(PcdCpuSmmSmrr2Base); mSmrr2Size = PcdGet32(PcdCpuSmmSmrr2Size); mSmrr2CacheType = PcdGet8 (PcdCpuSmmSmrr2CacheType); DEBUG ((DEBUG_INFO, "PcdCpuSmmSmrr2Base = %x\n", mSmrr2Base)); DEBUG ((DEBUG_INFO, "PcdCpuSmmSmrr2Size = %x\n", mSmrr2Size)); DEBUG ((DEBUG_INFO, "PcdCpuSmmSmrr2CacheType = %d\n", mSmrr2CacheType)); ASSERT((mSmrr2CacheType == MTRR_CACHE_WRITE_PROTECTED) || (mSmrr2CacheType == MTRR_CACHE_WRITE_BACK)); if (mSmrr2CacheType == MTRR_CACHE_WRITE_BACK) { // // PcdCpuSmmSmrr2Base and PcdCpuSmmSmrr2Size should be zero if cache type is WB // ASSERT (mSmrr2Base == 0); ASSERT (mSmrr2Size == 0); } mSmmTestRsvMemorySize = PcdGet32 (PcdSmmTestRsvMemorySize); FindSmramRange (); if (mSmmSpsPpam11Enable) { GetMsegInfo (); SpsConstructor (); } /// /// return EFI_SUCCESS; } /** Verifiy if SMRR base/size meet SMRR programming requirement. @param SmrrBase The base address of SMRR. @param SmrrSize The size of SMRR. @retval TRUE SMRR base/size is valid. @retval TRUE SMRR base/size is not valid. **/ BOOLEAN VerifySmrrBaseSize ( IN UINT32 SmrrBase, IN UINT32 SmrrSize ) { // // SMRR size cannot be less than 4-KBytes // SMRR size must be of length 2^n // SMRR base alignment cannot be less than SMRR length // if ((SmrrSize < SIZE_4KB) || (SmrrSize != GetPowerOfTwo32 (SmrrSize)) || ((SmrrBase & ~(SmrrSize - 1)) != SmrrBase)) { return FALSE; } return TRUE; } /** Return if SMRR is supported @retval TRUE SMRR is supported. @retval FALSE SMRR is not supported. **/ BOOLEAN IsSmrrSupported ( VOID ) { MSR_MTRRCAP_REGISTER MtrrCap; // // Use ArchMsr.h MSR_MTRRCAP + MSR_MTRRCAP_REGISTER consistently in this file // although the Smrr bit is also present in ArchitectureMsr.h MSR_IA32_MTRRCAP + // MSR_IA32_MTRRCAP_REGISTER. // MtrrCap.Uint64 = AsmReadMsr64 (MSR_MTRRCAP); return (BOOLEAN) (MtrrCap.Bits.Smrr != 0); } /** Return if SMRR2 is supported @retval TRUE SMRR2 is supported. @retval FALSE SMRR2 is not supported. **/ BOOLEAN IsSmrr2Supported ( VOID ) { MSR_MTRRCAP_REGISTER MtrrCap; // // Use ArchMsr.h MSR_MTRRCAP + MSR_MTRRCAP_REGISTER as the Smrr2 bit is // not present in ArchitectureMsr.h MSR_IA32_MTRRCAP + MSR_IA32_MTRRCAP_REGISTER. // MtrrCap.Uint64 = AsmReadMsr64 (MSR_MTRRCAP); return (BOOLEAN) (MtrrCap.Bits.Smrr2 != 0); } /** Return if SMRR lock is supported @retval TRUE SMRR lock is supported. @retval FALSE SMRR lock is not supported. **/ BOOLEAN IsSmrrLockSupported ( VOID ) { MSR_MTRRCAP_REGISTER MtrrCap; // // Use ArchMsr.h MSR_MTRRCAP + MSR_MTRRCAP_REGISTER as the SmrrLock bit is // not present in ArchitectureMsr.h MSR_IA32_MTRRCAP + MSR_IA32_MTRRCAP_REGISTER. // MtrrCap.Uint64 = AsmReadMsr64 (MSR_MTRRCAP); return (BOOLEAN) (MtrrCap.Bits.SmrrLock != 0); } /** Initialize SMRR in SMM relocate. @param SmrrBase The base address of SMRR. @param SmrrSize The size of SMRR. @param IsMonarch TRUE if the current CPU is Monarch. FALSE if the current CPU is not Monarch. **/ VOID NehalemInitSmrr ( IN UINT32 SmrrBase, IN UINT32 SmrrSize, IN BOOLEAN IsMonarch ) { UINT32 Smrr2Base; UINT32 Smrr2Size; UINT32 NewSmrrSize; UINT32 Alignment; MSR_SMRR_BASE_REGISTER MsrSmrrBase; MSR_SMRR_MASK_REGISTER MsrSmrrMask; if (!VerifySmrrBaseSize (SmrrBase, SmrrSize)) { // // If SMRR base/size does not meet requirment, check if we could enable SMRR2 // Smrr2Base = 0; Smrr2Size = 0; if (IsSmrr2Supported ()) { // // mSmrr2CacheType should be WB // ASSERT (mSmrr2CacheType == MTRR_CACHE_WRITE_BACK); // // Calculate the alignment of the SMM base address. // Alignment = (UINT32)LShiftU64 (1, LowBitSet32 (SmrrBase)); if (Alignment >= SmrrSize) { Smrr2Size = SmrrSize - GetPowerOfTwo32 (SmrrSize); NewSmrrSize = SmrrSize - Smrr2Size; } else { Smrr2Size = SmrrSize - Alignment; NewSmrrSize = Alignment; } Smrr2Base = SmrrBase + NewSmrrSize; if (IsMonarch) { DEBUG ((DEBUG_INFO, "SmrrBase = %x, NewSmrrSize = %x\n", SmrrBase, NewSmrrSize)); DEBUG ((DEBUG_INFO, "Smrr2Base = %x, Smrr2Size = %x\n", Smrr2Base, Smrr2Size)); } mSmmSizeNotCoveredBySmrr = Smrr2Size; mSmmBaseNotCoveredBySmrr = Smrr2Base; } if (!VerifySmrrBaseSize (Smrr2Base, Smrr2Size)) { // // Verify SMRR2 base/size // if (IsMonarch) { // // Print message and halt if CPU is Monarch // DEBUG ((DEBUG_ERROR, "SMM Base/Size does not meet alignment/size requirement!\n")); } CpuDeadLoop (); } } // // Use CommonMsr.h MSR_SMRR_BASE + MSR_SMRR_BASE_REGISTER as the Memtype // bit width is not same with the Type bit width in ArchitectureMsr.h // MSR_IA32_SMRR_PHYSBASE + MSR_IA32_SMRR_PHYSBASE_REGISTER. // MsrSmrrBase.Uint64 = AsmReadMsr64 (MSR_SMRR_BASE); MsrSmrrBase.Bits.Memtype = MTRR_CACHE_WRITE_BACK; MsrSmrrBase.Bits.Base = (UINT32) (SmrrBase >> 12); AsmWriteMsr64 (MSR_SMRR_BASE, MsrSmrrBase.Uint64); // // Use CommonMsr.h MSR_SMRR_MASK + MSR_SMRR_MASK_REGISTER consistently in this file // (the L bit is not present in ArchitectureMsr.h // MSR_IA32_SMRR_PHYSMASK + MSR_IA32_SMRR_PHYSMASK_REGISTER). // // Valid bit will be set in ConfigSmrr() at first SMI. // MsrSmrrMask.Uint64 = AsmReadMsr64 (MSR_SMRR_MASK); MsrSmrrMask.Bits.Mask = (UINT32) (~(SmrrSize - 1) >> 12); AsmWriteMsr64 (MSR_SMRR_MASK, MsrSmrrMask.Uint64); } /** Caculate SMRR2 base/size. @param SmrrBase The base address of SMRR. @param SmrrSize The size of SMRR. @param IsMonarch TRUE if the current CPU is Monarch. FALSE if the current CPU is not Monarch. **/ VOID HaswellCalculateSmrr2 ( IN UINT32 SmrrBase, IN UINT32 SmrrSize, IN BOOLEAN IsMonarch ) { UINTN Index; BOOLEAN Found; UINT32 Smrr2Base; UINT32 Smrr2Size; UINT32 CpuStart; UINT32 PhysicalSize; Smrr2Base = 0; Smrr2Size = 0; do { Found = FALSE; for (Index = 0; Index < mSmmFeatureSmramRangeCount; Index++) { CpuStart = (UINT32) mSmmFeatureSmramRanges[Index].CpuStart; PhysicalSize = (UINT32) mSmmFeatureSmramRanges[Index].PhysicalSize; if ((CpuStart < SmrrBase) || (CpuStart >= SmrrBase + SmrrSize)) { // // If one SMM range is out of current SMRR range // if (Smrr2Size == 0) { // // Assign the first SMRR2 range // Smrr2Base = CpuStart; Smrr2Size = PhysicalSize; Found = TRUE; } else if ((Smrr2Base + Smrr2Size) == CpuStart) { // // Append new SMM range at end of current SMRR2 range // Smrr2Size += PhysicalSize; Found = TRUE; } else if (Smrr2Base == (CpuStart + PhysicalSize)) { // // Insert new SMM range in front of current SMRR2 range // Smrr2Base -= PhysicalSize; Smrr2Size += PhysicalSize; Found = TRUE; } } } } while (Found); if (IsMonarch) { DEBUG ((DEBUG_INFO, "SMRR2 Base: 0x%x, SMRR2 Size: 0x%x\n", Smrr2Base, Smrr2Size)); } if (Smrr2Size != 0 && mSmmSizeNotCoveredBySmrr != 0) { // // We cannot support 2 SMRR2 range // if (IsMonarch) { DEBUG ((DEBUG_ERROR, "Can not support 2 SMRR2 ranges!\n")); } CpuDeadLoop (); } if (Smrr2Size == 0 && mSmmSizeNotCoveredBySmrr == 0) { // // If there is no SMRR2 requirement for WB memory, the SMRR2 range // mSmrr2Base and mSmrr2Size should be from PCDs in SmmCpuFeaturesLibConstructor() // ASSERT (mSmrr2CacheType == MTRR_CACHE_WRITE_PROTECTED); } else { ASSERT (mSmrr2CacheType == MTRR_CACHE_WRITE_BACK); if (mSmmSizeNotCoveredBySmrr != 0) { mSmrr2Base = mSmmBaseNotCoveredBySmrr; mSmrr2Size = mSmmSizeNotCoveredBySmrr; } else { // // Smrr2Size != 0 // mSmrr2Base = Smrr2Base; mSmrr2Size = Smrr2Size; } } if (!VerifySmrrBaseSize (mSmrr2Base, mSmrr2Size)) { if (IsMonarch) { DEBUG ((DEBUG_ERROR, "SMRR2 range does not meet SMRR2 requirement!\n")); } CpuDeadLoop (); } } /** Configure SMRR register at each SMM entry. **/ VOID NehalemConfigSmrr ( VOID ) { MSR_SMRR_MASK_REGISTER MsrSmrrMask; // // Use CommonMsr.h MSR_SMRR_MASK + MSR_SMRR_MASK_REGISTER as the L bit is not present // in ArchitectureMsr.h MSR_IA32_SMRR_PHYSMASK + MSR_IA32_SMRR_PHYSMASK_REGISTER. // MsrSmrrMask.Uint64 = AsmReadMsr64 (MSR_SMRR_MASK); if (mSmrrLockSupported) { if (MsrSmrrMask.Bits.L == 0) { MsrSmrrMask.Bits.Vld = 1; MsrSmrrMask.Bits.L = 1; AsmWriteMsr64 (MSR_SMRR_MASK, MsrSmrrMask.Uint64); } } else { if (MsrSmrrMask.Bits.Vld == 0) { MsrSmrrMask.Bits.Vld = 1; AsmWriteMsr64 (MSR_SMRR_MASK, MsrSmrrMask.Uint64); } } } /** Config MSR Save State feature for Haswell processors. @param[in] ProcessorNumber The processor number. **/ VOID HaswellConfigMsrSaveState ( IN UINTN ProcessorNumber ) { MSR_SMM_MCA_CAP_REGISTER SmmMcaCapMsr; MSR_SMM_FEATURE_CONTROL_REGISTER SmmFeatureControlMsr; if (mSmmMsrSaveStateEnable && mSmmFeatureControlSupported) { SmmMcaCapMsr.Uint64 = AsmReadMsr64 (MSR_SMM_MCA_CAP); if (SmmMcaCapMsr.Bits.SmmCpuSvrstr != 0) { SmmFeatureControlMsr.Uint64 = SmmReadReg64 (ProcessorNumber, SmmRegFeatureControl); ASSERT (SmmFeatureControlMsr.Bits.Lock == 0); if (SmmFeatureControlMsr.Bits.SmmCpuSaveEn == 0) { // // SMM_CPU_SAVE_EN_BIT is package scope, so when a thead in a package sets this bit, // MSR Save State feature will be enabled for all threads in the package in subsequent SMIs. // // Lock bit is not set here. It will be set when configuring SMM Code Access Check feature later. // SmmFeatureControlMsr.Bits.SmmCpuSaveEn = 1; SmmWriteReg64 (ProcessorNumber, SmmRegFeatureControl, SmmFeatureControlMsr.Uint64); mSaveStateInMsr = TRUE; } } } } /** Initialize SMRR2 in SMM relocate. @param Smrr2Base The base address of SMRR2. @param Smrr2Size The size of SMRR2. **/ VOID HaswellInitSmrr2 ( IN UINT32 Smrr2Base, IN UINT32 Smrr2Size ) { MSR_SMRR2_MASK_REGISTER MsrSmrr2Mask; MSR_SMRR2_BASE_REGISTER MsrSmrr2Base; ASSERT (Smrr2Size != 0); MsrSmrr2Mask.Uint64 = AsmReadMsr64 (MSR_SMRR2_MASK); if (mSmrrLockSupported == TRUE) { if (MsrSmrr2Mask.Bits.L == 0) { // // Program SMRR2 Base and Mask // MsrSmrr2Base.Uint64 = AsmReadMsr64 (MSR_SMRR2_BASE); MsrSmrr2Base.Bits.Memtype = mSmrr2CacheType; MsrSmrr2Base.Bits.Base = (UINT32) (Smrr2Base >> 12); AsmWriteMsr64 (MSR_SMRR2_BASE, MsrSmrr2Base.Uint64); MsrSmrr2Mask.Bits.Vld = 1; MsrSmrr2Mask.Bits.L = 1; MsrSmrr2Mask.Bits.Mask = (UINT32) (~(Smrr2Size - 1) >> 12); AsmWriteMsr64 (MSR_SMRR2_MASK, MsrSmrr2Mask.Uint64); } } else { if (MsrSmrr2Mask.Bits.Vld == 0) { // // Program SMRR2 Base and Mask // MsrSmrr2Base.Uint64 = AsmReadMsr64 (MSR_SMRR2_BASE); MsrSmrr2Base.Bits.Memtype = mSmrr2CacheType; MsrSmrr2Base.Bits.Base = (UINT32) (Smrr2Base >> 12); AsmWriteMsr64 (MSR_SMRR2_BASE, MsrSmrr2Base.Uint64); MsrSmrr2Mask.Bits.Vld = 1; MsrSmrr2Mask.Bits.Mask = (UINT32) (~(Smrr2Size - 1) >> 12); AsmWriteMsr64 (MSR_SMRR2_MASK, MsrSmrr2Mask.Uint64); } } } //////// // Below section is external function //////// /** Read MSR or CSR based on the CPU type Register to read. NOTE: Since platform may uses I/O ports 0xCF8 and 0xCFC to access CSR, we need to use SPIN_LOCK to avoid collision on MP System. @param[in] CpuIndex The processor index. @param[in] RegName Register name. @return 64-bit value read from register. **/ UINT64 SmmReadReg64 ( IN UINTN CpuIndex, IN SMM_REG_NAME RegName ) { UINT64 RetVal; RetVal = 0; switch (RegName) { // // Client uses MSR // case SmmRegFeatureControl: RetVal = AsmReadMsr64 (MSR_SMM_FEATURE_CONTROL); break; case SmmRegSmmEnable: RetVal = 0; // Targeted SMI--disabling specific CPU cores to respond enter into SMI-- is not supported in Client BIOS. break; case SmmRegSmmDelayed: RetVal = MsrGetSmmDelayed (); break; case SmmRegSmmBlocked: RetVal = MsrGetSmmBlocked (); break; default: ASSERT (FALSE); } return RetVal; } /** Write MSR or CSR based on the CPU type Register to write. NOTE: Since platform may uses I/O ports 0xCF8 and 0xCFC to access CSR, we need to use SPIN_LOCK to avoid collision on MP System. @param[in] CpuIndex The processor index. @param[in] RegName Register name. @param[in] RegValue 64-bit Register value. **/ VOID SmmWriteReg64 ( IN UINTN CpuIndex, IN SMM_REG_NAME RegName, IN UINT64 RegValue ) { switch (RegName) { // // Client uses MSR // case SmmRegFeatureControl: AsmWriteMsr64 (MSR_SMM_FEATURE_CONTROL, RegValue); break; case SmmRegSmmEnable: break; // Targeted SMI--disabling specific CPU cores to respond enter into SMI-- is not supported in Client BIOS. default: ASSERT (FALSE); } } /** Disable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs() returns TRUE. **/ VOID EFIAPI SmmCpuFeaturesDisableSmrr ( VOID ) { } /** Enable SMRR register if SMRR is supported and SmmCpuFeaturesNeedConfigureMtrrs() returns TRUE. **/ VOID EFIAPI SmmCpuFeaturesReenableSmrr ( VOID ) { } /** Determines if MTRR registers must be configured to set SMRAM cache-ability when executing in System Management Mode. @retval TRUE MTRR registers must be configured to set SMRAM cache-ability. @retval FALSE MTRR registers do not need to be configured to set SMRAM cache-ability. **/ BOOLEAN EFIAPI SmmCpuFeaturesNeedConfigureMtrrs ( VOID ) { return FALSE; } /** This function sets DR6 & DR7 according to SMM save state, before running SMM C code. They are useful when you want to enable hardware breakpoints in SMM without entry SMM mode. NOTE: It might not be appreciated in runtime since it might conflict with OS debugging facilities. Turn them off in RELEASE. @param CpuIndex CPU Index **/ VOID EFIAPI SmmFeatureCpuSmmDebugEntry ( IN UINTN CpuIndex ) { SMRAM_SAVE_STATE_MAP *CpuSaveState; if (FeaturePcdGet (PcdCpuSmmDebug)) { if (mSaveStateInMsr) { AsmWriteDr6 ((UINTN)AsmReadMsr64 (MSR_SMRAM_DR6)); AsmWriteDr7 ((UINTN)AsmReadMsr64 (MSR_SMRAM_DR7)); } else { CpuSaveState = (SMRAM_SAVE_STATE_MAP *)((UINTN)mSmBase[CpuIndex] + SMRAM_SAVE_STATE_MAP_OFFSET); if (mSmmFeatureSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) { AsmWriteDr6 (CpuSaveState->x86._DR6); AsmWriteDr7 (CpuSaveState->x86._DR7); } else { AsmWriteDr6 ((UINTN)CpuSaveState->x64._DR6); AsmWriteDr7 ((UINTN)CpuSaveState->x64._DR7); } } } } /** This function restores DR6 & DR7 to SMM save state. NOTE: It might not be appreciated in runtime since it might conflict with OS debugging facilities. Turn them off in RELEASE. @param CpuIndex CPU Index **/ VOID EFIAPI SmmFeatureCpuSmmDebugExit ( IN UINTN CpuIndex ) { SMRAM_SAVE_STATE_MAP *CpuSaveState; if (FeaturePcdGet (PcdCpuSmmDebug)) { if (mSaveStateInMsr) { // DR6/DR7 MSR is RO } else { CpuSaveState = (SMRAM_SAVE_STATE_MAP *)((UINTN)mSmBase[CpuIndex] + SMRAM_SAVE_STATE_MAP_OFFSET); if (mSmmFeatureSaveStateRegisterLma == EFI_SMM_SAVE_STATE_REGISTER_LMA_32BIT) { CpuSaveState->x86._DR7 = (UINT32)AsmReadDr7 (); CpuSaveState->x86._DR6 = (UINT32)AsmReadDr6 (); } else { CpuSaveState->x64._DR7 = AsmReadDr7 (); CpuSaveState->x64._DR6 = AsmReadDr6 (); } } } } /** Processor specific hook point each time a CPU enters System Management Mode. @param[in] CpuIndex The index of the CPU that has entered SMM. The value must be between 0 and the NumberOfCpus field in the System Management System Table (SMST). **/ VOID EFIAPI SmmCpuFeaturesRendezvousEntry ( IN UINTN CpuIndex ) { MSR_IA32_RTIT_CTL_REGISTER RtitCtl; if (!IsSecondaryThread ()) { // // Configure SMRR register at each SMM entry. // if (mFeatureContext.SmrrEnabled) { NehalemConfigSmrr (); } if (mFeatureContext.Smrr2Enabled) { // // Note that SMRR2 registers can't be written in the first SMI because // MSR Save State feature is not enabled. // HaswellInitSmrr2 (mSmrr2Base, mSmrr2Size); } } // // Configure ProcTrace feature at SMM entry. // if (mSmmProcTraceEnable) { RtitCtl.Uint64 = AsmReadMsr64 (MSR_IA32_RTIT_CTL); RtitCtl.Bits.TraceEn = 1; AsmWriteMsr64 (MSR_IA32_RTIT_CTL, RtitCtl.Uint64); } } /** Processor specific hook point each time a CPU exits System Management Mode. @param[in] CpuIndex The index of the CPU that is exiting SMM. The value must be between 0 and the NumberOfCpus field in the System Management System Table (SMST). **/ VOID EFIAPI SmmCpuFeaturesRendezvousExit ( IN UINTN CpuIndex ) { MSR_IA32_RTIT_CTL_REGISTER RtitCtl; // // Configure ProcTrace feature at SMM Exit. // if (mSmmProcTraceEnable) { RtitCtl.Uint64 = AsmReadMsr64(MSR_IA32_RTIT_CTL); RtitCtl.Bits.TraceEn = 0; AsmWriteMsr64 (MSR_IA32_RTIT_CTL, RtitCtl.Uint64); } } /** Hook point in normal execution mode that allows the one CPU that was elected as monarch during System Management Mode initialization to perform additional initialization actions immediately after all of the CPUs have processed their first SMI and called SmmCpuFeaturesInitializeProcessor() relocating SMBASE into a buffer in SMRAM and called SmmCpuFeaturesHookReturnFromSmm(). **/ VOID EFIAPI SmmCpuFeaturesSmmRelocationComplete ( VOID ) { // // Dump some SMM feature setting after SMM rellocation // DEBUG ((EFI_D_INFO, "CpuSmmFeatureControlSupported= %d\n", mSmmFeatureControlSupported)); DEBUG ((EFI_D_INFO, "CpuSmmSaveStateInMsr = %d\n", mSaveStateInMsr)); DEBUG ((EFI_D_INFO, "CpuSmmProtectedModeEnable = %d\n", mSmmProtectedModeEnable)); DEBUG ((EFI_D_INFO, "CpuSmmUseDelayIndication = %d\n", mSmmUseDelayIndication)); DEBUG ((EFI_D_INFO, "CpuSmmUseBlockIndication = %d\n", mSmmUseBlockIndication)); DEBUG ((EFI_D_INFO, "CpuSmmUseSmmEnableIndication = %d\n", mSmmEnableIndication)); DEBUG ((EFI_D_INFO, "CpuSmmSupovrStateLock = %d\n", mSmmSupovrStateLock)); DEBUG ((EFI_D_INFO, "mSmmSpsPpam11Enable = %d\n", mSmmSpsPpam11Enable)); if (!IsSmrrSupported ()) { return; } mFeatureContext.SmrrEnabled = TRUE; if (IsSmrr2Supported () && mSmrr2Size != 0 && mSaveStateInMsr) { mFeatureContext.Smrr2Enabled = TRUE; } else { mFeatureContext.Smrr2Enabled = FALSE; } if (IsSmrrLockSupported ()) { mSmrrLockSupported = TRUE; } else { mSmrrLockSupported = FALSE; } DEBUG ((EFI_D_INFO, "SmrrLockSupported = %x\n", mSmrrLockSupported)); } /** Setup SMM Protected Mode context for processor. @param ProcessorNumber The processor number. @param SmBase The SMBASE value of the processor. @param StackAddress Stack address of the processor. @param GdtBase Gdt table base address of the processor. @param GdtSize Gdt table size of the processor. @param CodeSegment Code segment value. @param ProtModeIdt Pointer to protected-mode IDT descriptor. **/ VOID SetupSmmProtectedModeContext( IN UINTN ProcessorNumber, IN UINT32 SmBase, IN UINT32 StackAddress, IN UINTN GdtBase, IN UINTN GdtSize, IN UINT16 CodeSegment, IN IA32_DESCRIPTOR *ProtModeIdt ) { SMMSEG *ThreadSmmSeg; IA32_SEGMENT_DESCRIPTOR *GdtDescriptor; UINTN GdtEntryIndex; if (!mSmmProtectedModeEnable) { return; } // // Fill SMMSEG structure fields // ThreadSmmSeg = &mSmmProtModeContext[ProcessorNumber].SmmProtectedModeSMMSEG; ThreadSmmSeg->GDTRLimit = (UINT32)(GdtSize - 1); ThreadSmmSeg->GDTRBaseOffset = (UINT32)GdtBase - SmBase; ThreadSmmSeg->CSSelector = CodeSegment; ThreadSmmSeg->ESPOffset = StackAddress - SmBase; ThreadSmmSeg->IDTRLimit = ProtModeIdt->Limit; ThreadSmmSeg->IDTRBaseOffset = (UINT32)ProtModeIdt->Base - SmBase; // // Patch 32bit CS/SS segment base to SMBASE, SS = CS + 8 decided by hardware // GdtEntryIndex = ThreadSmmSeg->CSSelector / sizeof(IA32_SEGMENT_DESCRIPTOR); GdtDescriptor = (IA32_SEGMENT_DESCRIPTOR *)GdtBase; GdtDescriptor[GdtEntryIndex].Bits.BaseLow = (UINT16)0x0000FFFF & SmBase; GdtDescriptor[GdtEntryIndex].Bits.BaseMid = (UINT8)0x000000FF & (SmBase >> 16); GdtDescriptor[GdtEntryIndex].Bits.BaseHigh = (UINT8)0x000000FF & (SmBase >> 24); GdtDescriptor[GdtEntryIndex + 1].Bits.BaseLow = (UINT16)0x0000FFFF & SmBase; GdtDescriptor[GdtEntryIndex + 1].Bits.BaseMid = (UINT8)0x000000FF & (SmBase >> 16); GdtDescriptor[GdtEntryIndex + 1].Bits.BaseHigh = (UINT8)0x000000FF & (SmBase >> 24); } /** Enable SMM Protected Mode. @param ProcessorNumber The processor number. **/ VOID EnableSmmProtectedMode ( IN UINTN ProcessorNumber ) { MSR_SMM_MCA_CAP_REGISTER SmmMcaCapMsr; MSR_SMM_PROT_MODE_BASE_REGISTER SmmProtModeBaseMsr; SMMSEG *ThreadSmmSeg; // // Check if hardware support SMM PROT MODE feature // SmmMcaCapMsr.Uint64 = AsmReadMsr64 (MSR_SMM_MCA_CAP); if (SmmMcaCapMsr.Bits.SmmProtMode == 0) { mSmmProtectedModeEnable = FALSE; FreePages (mSmmProtModeContext, EFI_SIZE_TO_PAGES(sizeof(SMM_PROT_MODE_CONTEXT) * PcdGet32(PcdCpuMaxLogicalProcessorNumber))); return ; } // // Enable the SMM PROT MODE feature in the SMMSEG structure // ThreadSmmSeg = &mSmmProtModeContext[ProcessorNumber].SmmProtectedModeSMMSEG; ThreadSmmSeg->SmmSegFeatureEnables = (SMMSEG_FEATURE_ENABLE | SMMSEG_FEATURE_CR4_MCE_CTL_ENABLE); // // SMMSEG should be 256-byte aligned 32-bit address // SmmProtModeBaseMsr.Uint64 = (UINT64) (UINTN) ThreadSmmSeg; SmmProtModeBaseMsr.Bits.Enable = 1; AsmWriteMsr64 (MSR_SMM_PROT_MODE_BASE, SmmProtModeBaseMsr.Uint64); } /** Called during the very first SMI into System Management Mode to initialize CPU features, including SMBASE, for the currently executing CPU. Since this is the first SMI, the SMRAM Save State Map is at the default address of SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET. The currently executing CPU is specified by CpuIndex and CpuIndex can be used to access information about the currently executing CPU in the ProcessorInfo array and the HotPlugCpuData data structure. @param[in] CpuIndex The index of the CPU to initialize. The value must be between 0 and the NumberOfCpus field in the System Management System Table (SMST). @param[in] IsMonarch TRUE if the CpuIndex is the index of the CPU that was elected as monarch during System Management Mode initialization. FALSE if the CpuIndex is not the index of the CPU that was elected as monarch during System Management Mode initialization. @param[in] ProcessorInfo Pointer to an array of EFI_PROCESSOR_INFORMATION structures. ProcessorInfo[CpuIndex] contains the information for the currently executing CPU. @param[in] CpuHotPlugData Pointer to the CPU_HOT_PLUG_DATA structure that contains the ApidId and SmBase arrays. **/ VOID EFIAPI SmmCpuFeaturesInitializeProcessor ( IN UINTN CpuIndex, IN BOOLEAN IsMonarch, IN EFI_PROCESSOR_INFORMATION *ProcessorInfo, IN CPU_HOT_PLUG_DATA *CpuHotPlugData ) { SMRAM_SAVE_STATE_MAP *CpuState; MSR_SMM_MCA_CAP_REGISTER SmmMcaCapMsr; MSR_SMM_FEATURE_CONTROL_REGISTER SmmFeatureControl; UINT16 LogProcIndexInPackage; UINT32 ApicId; UINT32 IntraPackageIdBits; SMM_CPU_SYNC_FEATURE *SyncFeature; UINTN Cr0; STATIC BOOLEAN SmmTestRsvMemoryInit = FALSE; CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_EBX Ebx; MSR_SMRAM_SMBASE_REGISTER MsrSmramSmBase; mSmmProcessorInfo = ProcessorInfo; // // If requested, allocate SMM memory for test purposes only. // if (mSmmTestRsvMemorySize && !SmmTestRsvMemoryInit) { VOID *RsvRegion; DEBUG ((DEBUG_INFO, "\nPcdSmmTestRsvMemorySize = 0x%x\n", mSmmTestRsvMemorySize)); RsvRegion = AllocatePool (mSmmTestRsvMemorySize); if (RsvRegion == NULL) { DEBUG ((DEBUG_ERROR, "Can't allocate reserved memory range.\n")); } else { DEBUG ((DEBUG_INFO, "Base of test reserved memory = 0x%x", (UINT32)(UINTN)RsvRegion)); } SmmTestRsvMemoryInit = TRUE; } mSmBase[CpuIndex] = (UINT32)CpuHotPlugData->SmBase[CpuIndex]; SyncFeature = &(mSmmSyncFeature[CpuIndex]); SyncFeature->TargetedSmiSupported = FALSE; SyncFeature->DelayIndicationSupported = FALSE; SyncFeature->BlockIndicationSupported = FALSE; SyncFeature->HaswellLogProcEnBit = (UINT64)(INT64)(-1); mSmmInitSaveStateInMsr = FALSE; // // Configure SMBASE. // // // Check SMM Code Access Check bit before access SMM Feature Control MSR // SmmMcaCapMsr.Uint64 = AsmReadMsr64 (MSR_SMM_MCA_CAP); if (SmmMcaCapMsr.Bits.SmmCodeAccessChk != 0) { mSmmFeatureControlSupported = TRUE; SmmFeatureControl.Uint64 = SmmReadReg64 (CpuIndex, SmmRegFeatureControl); if (SmmMcaCapMsr.Bits.SmmCpuSvrstr != 0 && SmmFeatureControl.Bits.SmmCpuSaveEn != 0) { MsrSmramSmBase.Uint64 = AsmReadMsr64 (MSR_SMRAM_SMBASE); MsrSmramSmBase.Bits.Smbase = (UINT32) CpuHotPlugData->SmBase[CpuIndex]; AsmWriteMsr64 (MSR_SMRAM_SMBASE, MsrSmramSmBase.Uint64); mSmmInitSaveStateInMsr = TRUE; } } // // Fall back to legacy SMBASE setup. // if (!mSmmInitSaveStateInMsr) { CpuState = (SMRAM_SAVE_STATE_MAP *)(UINTN)(SMM_DEFAULT_SMBASE + SMRAM_SAVE_STATE_MAP_OFFSET); CpuState->x86.SMBASE = (UINT32)CpuHotPlugData->SmBase[CpuIndex]; } if (IsSmrrSupported ()) { NehalemInitSmrr (CpuHotPlugData->SmrrBase, CpuHotPlugData->SmrrSize, IsMonarch); } if (IsSmrr2Supported ()) { HaswellCalculateSmrr2 (CpuHotPlugData->SmrrBase, CpuHotPlugData->SmrrSize, IsMonarch); } HaswellConfigMsrSaveState (CpuIndex); if (IsSmmSyncFeatureSupported ()) { if (mSmmUseDelayIndication || mSmmUseBlockIndication || mSmmEnableIndication) { // // Get local APIC ID of the executing processor for logical processor Bitmask // C1T0 APIC ID = 2, bitmask = 0000 0100b // C2T1 APIC ID = 5, bitmask = 0010 0000b // ApicId = GetInitialApicId (); // // CPUID.(EAX=0Bh, ECX=01h) provides topology information for the Core level // AsmCpuidEx (CPUID_EXTENDED_TOPOLOGY, 1, &IntraPackageIdBits, NULL, NULL, NULL); IntraPackageIdBits &= 0x1f; LogProcIndexInPackage = ApicId & ((1 << IntraPackageIdBits) - 1); SyncFeature->HaswellLogProcEnBit = LShiftU64 (1ull, LogProcIndexInPackage); } } if (mSmmUseDelayIndication) { SyncFeature->DelayIndicationSupported = (BOOLEAN) (SmmMcaCapMsr.Bits.LongFlowIndication != 0); } if (mSmmUseBlockIndication) { SyncFeature->BlockIndicationSupported = TRUE; } if (mSmmEnableIndication) { SyncFeature->TargetedSmiSupported = (BOOLEAN) (SmmMcaCapMsr.Bits.TargetedSmi != 0); } // // Check ProcTrace Capability // AsmCpuidEx(CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, 0, NULL, &Ebx.Uint32, NULL, NULL); if (Ebx.Bits.IntelProcessorTrace == 0) { mSmmProcTraceEnable = FALSE; } if (MsrIsSmmSupovrStateLockSupported ()) { // // At this point in normal boot, supervisor state lock data being cleared. // At S3 resume time in SmmCpuFeaturesCompleteSmmReadyToLock() supervisor state lock data is set // to enable lock for paging state, SMBase, Monitor control. // Cr0 = AsmReadCr0(); AsmWriteCr0 (Cr0 & ~CR0_WP); if (mSmmSpsPpam11Enable) { gSmmSupovrStateLockData = 0; PatchSmmEntryPoint ((VOID *) (UINTN) (mSmBase[CpuIndex] + SMM_HANDLER_OFFSET), SMM_ENTRY_POINT_INFO_SUPOVR_STATE_LOCK, &gSmmSupovrStateLockData, sizeof(gSmmSupovrStateLockData)); gSmmFeatureRing3Supported = 0; PatchSmmEntryPoint ((VOID *) (UINTN) (mSmBase[CpuIndex] + SMM_HANDLER_OFFSET), SMM_ENTRY_POINT_INFO_USER_MODE_ENABLE, &gSmmFeatureRing3Supported, sizeof(gSmmFeatureRing3Supported)); } else { /// /// *(UINT64 *)(UINTN)(mSmBase[CpuIndex] + (UINTN)&gSmmSupovrStateLockData - (UINTN)gcSmmFeatureSmiHandlerTemplate + SMM_HANDLER_OFFSET) = 0; } AsmWriteCr0 (Cr0); mSmmSupovrStateLock = TRUE; if (!PcdGetBool(PcdCpuSmmRestrictedMemoryAccess)) { if (CpuIndex == 0) { DEBUG ((DEBUG_ERROR, "Unsupported Configuration - PcdCpuSmmRestrictedMemoryAccess is FALSE but SmmSupovrStateLock is TRUE\n")); } ASSERT (FALSE); } } if (mSmmProtectedModeEnable) { EnableSmmProtectedMode (CpuIndex); } if (CpuIndex == 0) { DEBUG((DEBUG_INFO, " Microcode ID (0x%08x) = 0x%016lx\n", MSR_IA32_BIOS_SIGN_ID, AsmReadMsr64 (MSR_IA32_BIOS_SIGN_ID))); DEBUG((DEBUG_INFO, " Platform ID (0x%08x) = 0x%016lx\n", MSR_IA32_PLATFORM_ID, AsmReadMsr64 (MSR_IA32_PLATFORM_ID))); DEBUG((DEBUG_INFO, " SMM_MCA_CAP (0x%08x) = 0x%016lx\n", MSR_SMM_MCA_CAP, AsmReadMsr64 (MSR_SMM_MCA_CAP))); } FinishSmmCpuFeaturesInitializeProcessor (); } /** Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is returned, then a custom SMI handler is not provided by this library, and the default SMI handler must be used. @retval 0 Use the default SMI handler. @retval > 0 Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler() The caller is required to allocate enough SMRAM for each CPU to support the size of the custom SMI handler. **/ UINTN EFIAPI SmmCpuFeaturesGetSmiHandlerSize ( VOID ) { if (mSmmSpsPpam11Enable) { return SmmCpuFeaturesGetSmiHandlerSizeSps (); } /// /// return gcSmmFeatureSmiHandlerSize; } /** Install a custom SMI handler for the CPU specified by CpuIndex. This function is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater than zero and is called by the CPU that was elected as monarch during System Management Mode initialization. @param[in] CpuIndex The index of the CPU to install the custom SMI handler. The value must be between 0 and the NumberOfCpus field in the System Management System Table (SMST). @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. @param[in] SmiStack The stack to use when an SMI is processed by the the CPU specified by CpuIndex. @param[in] StackSize The size, in bytes, if the stack used when an SMI is processed by the CPU specified by CpuIndex. @param[in] GdtBase The base address of the GDT to use when an SMI is processed by the CPU specified by CpuIndex. @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is processed by the CPU specified by CpuIndex. @param[in] IdtBase The base address of the IDT to use when an SMI is processed by the CPU specified by CpuIndex. @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is processed by the CPU specified by CpuIndex. @param[in] Cr3 The base address of the page tables to use when an SMI is processed by the CPU specified by CpuIndex. **/ VOID EFIAPI SmmCpuFeaturesInstallSmiHandler ( IN UINTN CpuIndex, IN UINT32 SmBase, IN VOID *SmiStack, IN UINTN StackSize, IN UINTN GdtBase, IN UINTN GdtSize, IN UINTN IdtBase, IN UINTN IdtSize, IN UINT32 Cr3 ) { if (mSmmSpsPpam11Enable) { SmmCpuFeaturesInstallSmiHandlerSps (CpuIndex, SmBase, SmiStack, StackSize, GdtBase, GdtSize, IdtBase, IdtSize, Cr3); return; } /// /// if (mSmmProtectedModeEnable) { // // Initialize protected mode IDT // InitProtectedModeIdt (Cr3); } // // Initialize values in template before copy // gSmmFeatureSmiStack = (UINT32)((UINTN)SmiStack + StackSize - sizeof (UINTN)); gSmmFeatureSmiCr3 = Cr3; gSmmFeatureSmiHandlerIdtr.Base = IdtBase; gSmmFeatureSmiHandlerIdtr.Limit = (UINT16)(IdtSize - 1); DEBUG ((DEBUG_INFO, "gSmmFeatureSmiHandlerIdtr - 0x%x\n", &gSmmFeatureSmiHandlerIdtr)); DEBUG ((DEBUG_INFO, "Base - 0x%x\n", gSmmFeatureSmiHandlerIdtr.Base)); DEBUG ((DEBUG_INFO, "Limit - 0x%x\n", gSmmFeatureSmiHandlerIdtr.Limit)); gGdtDesc.Base = (UINT32)GdtBase; gGdtDesc.Limit = (UINT16)(GdtSize - 1); // // Set the value at the top of the CPU stack to the CPU Index // *(UINTN*)(UINTN)gSmmFeatureSmiStack = CpuIndex; gPMStackDesc[0] = gSmmFeatureSmiStack; // // Copy template to CPU specific SMI handler location // if (mSmmProtectedModeEnable) { gProtModeSmbase = SmBase; CopyMem ( (VOID*)((UINTN)SmBase + SMM_HANDLER_OFFSET), (VOID*)gcSmmFeatureSmiHandlerTemplate, gcSmmFeatureSmiHandlerSize ); // // Prepare for the SMMSEG structure // SetupSmmProtectedModeContext ( CpuIndex, (UINT32)SmBase, gSmmFeatureSmiStack, GdtBase, GdtSize, SMMSEG_PROTECT_MODE_CODE_SEGMENT, (IA32_DESCRIPTOR *)(UINTN)gProtModeIdtr ); } else { gSmmFeatureSmbase = SmBase; CopyMem ( (VOID*)((UINTN)SmBase + SMM_HANDLER_OFFSET), (VOID*)gcSmmFeatureSmiHandlerTemplate, gcSmmFeatureSmiHandlerSize ); } } /** Check to see if an SMM register is supported by a specified CPU. @param[in] CpuIndex The index of the CPU to check for SMM register support. The value must be between 0 and the NumberOfCpus field in the System Management System Table (SMST). @param[in] RegName Identifies the SMM register to check for support. @retval TRUE The SMM register specified by RegName is supported by the CPU specified by CpuIndex. @retval FALSE The SMM register specified by RegName is not supported by the CPU specified by CpuIndex. **/ BOOLEAN EFIAPI SmmCpuFeaturesIsSmmRegisterSupported ( IN UINTN CpuIndex, IN SMM_REG_NAME RegName ) { switch (RegName) { case SmmRegFeatureControl: return mSmmFeatureControlSupported; case SmmRegSmmEnable: return mSmmSyncFeature[CpuIndex].TargetedSmiSupported; case SmmRegSmmDelayed: return mSmmSyncFeature[CpuIndex].DelayIndicationSupported; case SmmRegSmmBlocked: return mSmmSyncFeature[CpuIndex].BlockIndicationSupported; } return FALSE; } /** Returns the current value of the SMM register for the specified CPU. If the SMM register is not supported, then 0 is returned. @param[in] CpuIndex The index of the CPU to read the SMM register. The value must be between 0 and the NumberOfCpus field in the System Management System Table (SMST). @param[in] RegName Identifies the SMM register to read. @return The value of the SMM register specified by RegName from the CPU specified by CpuIndex. **/ UINT64 EFIAPI SmmCpuFeaturesGetSmmRegister ( IN UINTN CpuIndex, IN SMM_REG_NAME RegName ) { if (SmmCpuFeaturesIsSmmRegisterSupported (CpuIndex, RegName)) { if (RegName == SmmRegFeatureControl) { return SmmReadReg64 (CpuIndex, RegName); } if (IsSmmSyncFeatureSupported ()) { if ((SmmReadReg64 (CpuIndex, RegName) & mSmmSyncFeature[CpuIndex].HaswellLogProcEnBit) != 0) { return 1; } } } return 0; } /** Sets the value of an SMM register on a specified CPU. If the SMM register is not supported, then no action is performed. @param[in] CpuIndex The index of the CPU to write the SMM register. The value must be between 0 and the NumberOfCpus field in the System Management System Table (SMST). @param[in] RegName Identifies the SMM register to write. registers are read-only. @param[in] Value The value to write to the SMM register. **/ VOID EFIAPI SmmCpuFeaturesSetSmmRegister ( IN UINTN CpuIndex, IN SMM_REG_NAME RegName, IN UINT64 Value ) { UINT64 NewValue; if (SmmCpuFeaturesIsSmmRegisterSupported (CpuIndex, RegName)) { if (RegName == SmmRegFeatureControl) { SmmWriteReg64 (CpuIndex, RegName, Value); } else { NewValue = SmmReadReg64 (CpuIndex, RegName); if (IsSmmSyncFeatureSupported ()) { if (Value != 0) { NewValue = NewValue | mSmmSyncFeature[CpuIndex].HaswellLogProcEnBit; } else { NewValue = NewValue & (~mSmmSyncFeature[CpuIndex].HaswellLogProcEnBit); } } SmmWriteReg64 (CpuIndex, RegName, NewValue); } } } /** This function is hook point called after the gEfiSmmReadyToLockProtocolGuid notification is completely processed. **/ VOID EFIAPI SmmCpuFeaturesCompleteSmmReadyToLock ( VOID ) { UINTN Index; MSR_SMM_SUPOVR_STATE_LOCK_REGISTER SupovrStateLock; DEBUG ((EFI_D_INFO, "SmmCpuFeaturesCompleteSmmReadyToLock...\n")); // // Disable write protection, because we need mark page table to be write protected. // We need *write* page table memory, to mark itself to be *read only*. // AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP); if (mSmmSupovrStateLock) { DEBUG ((DEBUG_INFO, "Patch SmmSupovrStateLock\n")); for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { SupovrStateLock.Uint64 = 0; SupovrStateLock.Bits.PagingStates = 1; SupovrStateLock.Bits.Smbase = 1; SupovrStateLock.Bits.Ia32SmmMonitorCtlMsr = SmmSupovrStateLockPatch (); #if FixedPcdGetBool (PcdSpaEnable) == 1 if (gSpaEnable) { SupovrStateLock.Bits.PagingStates = 0; } #endif if (mSmmSpsPpam11Enable) { gSmmSupovrStateLockData = SupovrStateLock.Uint64; PatchSmmEntryPoint ((VOID *)(UINTN)(mSmBase[Index] + SMM_HANDLER_OFFSET), SMM_ENTRY_POINT_INFO_SUPOVR_STATE_LOCK, &gSmmSupovrStateLockData, sizeof(gSmmSupovrStateLockData)); gSmmFeatureRing3Supported = 1; PatchSmmEntryPoint ((VOID *)(UINTN)(mSmBase[Index] + SMM_HANDLER_OFFSET), SMM_ENTRY_POINT_INFO_USER_MODE_ENABLE, &gSmmFeatureRing3Supported, sizeof(gSmmFeatureRing3Supported)); } /// /// } } if (mSmmSpsPpam11Enable) { SetSmmDgrPolicy (); } // // Enable write protection, after page table updated. // AsmWriteCr0 (AsmReadCr0 () | CR0_WP); DEBUG ((EFI_D_INFO, "SmmCpuFeaturesCompleteSmmReadyToLock Done\n")); } /** This API provides a method for a CPU to allocate a specific region for storing page tables. This API can be called more than once to allocate the page table memory. Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL is returned. If there is not enough memory remaining to satisfy the request, then NULL is returned. This function can also return NULL if there is no preference on where the page tables are allocated in SMRAM. @param Pages The number of 4 KB pages to allocate. @return A pointer to the allocated buffer for page tables. @retval NULL Fail to allocate a specific region for storing page tables, Or there is no preference on where the page tables are allocated in SMRAM. **/ VOID * EFIAPI SmmCpuFeaturesAllocatePageTableMemory ( IN UINTN Pages ) { if (Pages == 0) { return NULL; } return NULL; }