AMD does not support MSR_IA32_MISC_ENABLE. Accessing that register causes and exception on AMD processors. If Execution Disable is supported, but if the processor is an AMD processor, skip manipulating MSR_IA32_MISC_ENABLE[34] XD Disable bit. Cc: Eric Dong <eric.dong@intel.com> Cc: Ray Ni <ray.ni@intel.com> Cc: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Garrett Kirkendall <garrett.kirkendall@amd.com> Message-Id: <20200622131825.1352-5-Garrett.Kirkendall@amd.com> Reviewed-by: Laszlo Ersek <lersek@redhat.com> Tested-by: Laszlo Ersek <lersek@redhat.com> Reviewed-by: Eric Dong <eric.dong@intel.com>
		
			
				
	
	
		
			1518 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1518 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| Enable SMM profile.
 | |
| 
 | |
| Copyright (c) 2012 - 2019, Intel Corporation. All rights reserved.<BR>
 | |
| Copyright (c) 2017 - 2020, AMD Incorporated. All rights reserved.<BR>
 | |
| 
 | |
| SPDX-License-Identifier: BSD-2-Clause-Patent
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "PiSmmCpuDxeSmm.h"
 | |
| #include "SmmProfileInternal.h"
 | |
| 
 | |
| UINT32                    mSmmProfileCr3;
 | |
| 
 | |
| SMM_PROFILE_HEADER        *mSmmProfileBase;
 | |
| MSR_DS_AREA_STRUCT        *mMsrDsAreaBase;
 | |
| //
 | |
| // The buffer to store SMM profile data.
 | |
| //
 | |
| UINTN                     mSmmProfileSize;
 | |
| 
 | |
| //
 | |
| // The buffer to enable branch trace store.
 | |
| //
 | |
| UINTN                     mMsrDsAreaSize   = SMM_PROFILE_DTS_SIZE;
 | |
| 
 | |
| //
 | |
| // The flag indicates if execute-disable is supported by processor.
 | |
| //
 | |
| BOOLEAN                   mXdSupported     = TRUE;
 | |
| 
 | |
| //
 | |
| // The flag indicates if execute-disable is enabled on processor.
 | |
| //
 | |
| BOOLEAN                   mXdEnabled       = FALSE;
 | |
| 
 | |
| //
 | |
| // The flag indicates if BTS is supported by processor.
 | |
| //
 | |
| BOOLEAN                   mBtsSupported     = TRUE;
 | |
| 
 | |
| //
 | |
| // The flag indicates if SMM profile starts to record data.
 | |
| //
 | |
| BOOLEAN                   mSmmProfileStart = FALSE;
 | |
| 
 | |
| //
 | |
| // The flag indicates if #DB will be setup in #PF handler.
 | |
| //
 | |
| BOOLEAN                   mSetupDebugTrap = FALSE;
 | |
| 
 | |
| //
 | |
| // Record the page fault exception count for one instruction execution.
 | |
| //
 | |
| UINTN                     *mPFEntryCount;
 | |
| 
 | |
| UINT64                    (*mLastPFEntryValue)[MAX_PF_ENTRY_COUNT];
 | |
| UINT64                    *(*mLastPFEntryPointer)[MAX_PF_ENTRY_COUNT];
 | |
| 
 | |
| MSR_DS_AREA_STRUCT        **mMsrDsArea;
 | |
| BRANCH_TRACE_RECORD       **mMsrBTSRecord;
 | |
| UINTN                     mBTSRecordNumber;
 | |
| PEBS_RECORD               **mMsrPEBSRecord;
 | |
| 
 | |
| //
 | |
| // These memory ranges are always present, they does not generate the access type of page fault exception,
 | |
| // but they possibly generate instruction fetch type of page fault exception.
 | |
| //
 | |
| MEMORY_PROTECTION_RANGE   *mProtectionMemRange     = NULL;
 | |
| UINTN                     mProtectionMemRangeCount = 0;
 | |
| 
 | |
| //
 | |
| // Some predefined memory ranges.
 | |
| //
 | |
| MEMORY_PROTECTION_RANGE mProtectionMemRangeTemplate[] = {
 | |
|   //
 | |
|   // SMRAM range (to be fixed in runtime).
 | |
|   // It is always present and instruction fetches are allowed.
 | |
|   //
 | |
|   {{0x00000000, 0x00000000},TRUE,FALSE},
 | |
| 
 | |
|   //
 | |
|   // SMM profile data range( to be fixed in runtime).
 | |
|   // It is always present and instruction fetches are not allowed.
 | |
|   //
 | |
|   {{0x00000000, 0x00000000},TRUE,TRUE},
 | |
| 
 | |
|   //
 | |
|   // SMRAM ranges not covered by mCpuHotPlugData.SmrrBase/mCpuHotPlugData.SmrrSiz (to be fixed in runtime).
 | |
|   // It is always present and instruction fetches are allowed.
 | |
|   // {{0x00000000, 0x00000000},TRUE,FALSE},
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // Future extended range could be added here.
 | |
|   //
 | |
| 
 | |
|   //
 | |
|   // PCI MMIO ranges (to be added in runtime).
 | |
|   // They are always present and instruction fetches are not allowed.
 | |
|   //
 | |
| };
 | |
| 
 | |
| //
 | |
| // These memory ranges are mapped by 4KB-page instead of 2MB-page.
 | |
| //
 | |
| MEMORY_RANGE              *mSplitMemRange          = NULL;
 | |
| UINTN                     mSplitMemRangeCount      = 0;
 | |
| 
 | |
| //
 | |
| // SMI command port.
 | |
| //
 | |
| UINT32                    mSmiCommandPort;
 | |
| 
 | |
| /**
 | |
|   Disable branch trace store.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| DisableBTS (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   AsmMsrAnd64 (MSR_DEBUG_CTL, ~((UINT64)(MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR)));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable branch trace store.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| EnableBTS (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   AsmMsrOr64 (MSR_DEBUG_CTL, (MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get CPU Index from APIC ID.
 | |
| 
 | |
| **/
 | |
| UINTN
 | |
| GetCpuIndex (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   UINTN     Index;
 | |
|   UINT32    ApicId;
 | |
| 
 | |
|   ApicId = GetApicId ();
 | |
| 
 | |
|   for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
 | |
|     if (gSmmCpuPrivate->ProcessorInfo[Index].ProcessorId == ApicId) {
 | |
|       return Index;
 | |
|     }
 | |
|   }
 | |
|   ASSERT (FALSE);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get the source of IP after execute-disable exception is triggered.
 | |
| 
 | |
|   @param  CpuIndex        The index of CPU.
 | |
|   @param  DestinationIP   The destination address.
 | |
| 
 | |
| **/
 | |
| UINT64
 | |
| GetSourceFromDestinationOnBts (
 | |
|   UINTN  CpuIndex,
 | |
|   UINT64 DestinationIP
 | |
|   )
 | |
| {
 | |
|   BRANCH_TRACE_RECORD  *CurrentBTSRecord;
 | |
|   UINTN                Index;
 | |
|   BOOLEAN              FirstMatch;
 | |
| 
 | |
|   FirstMatch = FALSE;
 | |
| 
 | |
|   CurrentBTSRecord = (BRANCH_TRACE_RECORD *)mMsrDsArea[CpuIndex]->BTSIndex;
 | |
|   for (Index = 0; Index < mBTSRecordNumber; Index++) {
 | |
|     if ((UINTN)CurrentBTSRecord < (UINTN)mMsrBTSRecord[CpuIndex]) {
 | |
|       //
 | |
|       // Underflow
 | |
|       //
 | |
|       CurrentBTSRecord = (BRANCH_TRACE_RECORD *)((UINTN)mMsrDsArea[CpuIndex]->BTSAbsoluteMaximum - 1);
 | |
|       CurrentBTSRecord --;
 | |
|     }
 | |
|     if (CurrentBTSRecord->LastBranchTo == DestinationIP) {
 | |
|       //
 | |
|       // Good! find 1st one, then find 2nd one.
 | |
|       //
 | |
|       if (!FirstMatch) {
 | |
|         //
 | |
|         // The first one is DEBUG exception
 | |
|         //
 | |
|         FirstMatch = TRUE;
 | |
|       } else {
 | |
|         //
 | |
|         // Good find proper one.
 | |
|         //
 | |
|         return CurrentBTSRecord->LastBranchFrom;
 | |
|       }
 | |
|     }
 | |
|     CurrentBTSRecord--;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   SMM profile specific INT 1 (single-step) exception handler.
 | |
| 
 | |
|   @param  InterruptType    Defines the type of interrupt or exception that
 | |
|                            occurred on the processor.This parameter is processor architecture specific.
 | |
|   @param  SystemContext    A pointer to the processor context when
 | |
|                            the interrupt occurred on the processor.
 | |
| **/
 | |
| VOID
 | |
| EFIAPI
 | |
| DebugExceptionHandler (
 | |
|     IN EFI_EXCEPTION_TYPE   InterruptType,
 | |
|     IN EFI_SYSTEM_CONTEXT   SystemContext
 | |
|   )
 | |
| {
 | |
|   UINTN  CpuIndex;
 | |
|   UINTN  PFEntry;
 | |
| 
 | |
|   if (!mSmmProfileStart &&
 | |
|       !HEAP_GUARD_NONSTOP_MODE &&
 | |
|       !NULL_DETECTION_NONSTOP_MODE) {
 | |
|     return;
 | |
|   }
 | |
|   CpuIndex = GetCpuIndex ();
 | |
| 
 | |
|   //
 | |
|   // Clear last PF entries
 | |
|   //
 | |
|   for (PFEntry = 0; PFEntry < mPFEntryCount[CpuIndex]; PFEntry++) {
 | |
|     *mLastPFEntryPointer[CpuIndex][PFEntry] = mLastPFEntryValue[CpuIndex][PFEntry];
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Reset page fault exception count for next page fault.
 | |
|   //
 | |
|   mPFEntryCount[CpuIndex] = 0;
 | |
| 
 | |
|   //
 | |
|   // Flush TLB
 | |
|   //
 | |
|   CpuFlushTlb ();
 | |
| 
 | |
|   //
 | |
|   // Clear TF in EFLAGS
 | |
|   //
 | |
|   ClearTrapFlag (SystemContext);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check if the input address is in SMM ranges.
 | |
| 
 | |
|   @param[in]  Address       The input address.
 | |
| 
 | |
|   @retval TRUE     The input address is in SMM.
 | |
|   @retval FALSE    The input address is not in SMM.
 | |
| **/
 | |
| BOOLEAN
 | |
| IsInSmmRanges (
 | |
|   IN EFI_PHYSICAL_ADDRESS   Address
 | |
|   )
 | |
| {
 | |
|   UINTN  Index;
 | |
| 
 | |
|   if ((Address >= mCpuHotPlugData.SmrrBase) && (Address < mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize)) {
 | |
|     return TRUE;
 | |
|   }
 | |
|   for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) {
 | |
|     if (Address >= mSmmCpuSmramRanges[Index].CpuStart &&
 | |
|         Address < mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize) {
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check if the memory address will be mapped by 4KB-page.
 | |
| 
 | |
|   @param  Address  The address of Memory.
 | |
|   @param  Nx       The flag indicates if the memory is execute-disable.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| IsAddressValid (
 | |
|   IN EFI_PHYSICAL_ADDRESS   Address,
 | |
|   IN BOOLEAN                *Nx
 | |
|   )
 | |
| {
 | |
|   UINTN  Index;
 | |
| 
 | |
|   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
 | |
|     //
 | |
|     // Check configuration
 | |
|     //
 | |
|     for (Index = 0; Index < mProtectionMemRangeCount; Index++) {
 | |
|       if ((Address >= mProtectionMemRange[Index].Range.Base) && (Address < mProtectionMemRange[Index].Range.Top)) {
 | |
|         *Nx = mProtectionMemRange[Index].Nx;
 | |
|         return mProtectionMemRange[Index].Present;
 | |
|       }
 | |
|     }
 | |
|     *Nx = TRUE;
 | |
|     return FALSE;
 | |
| 
 | |
|   } else {
 | |
|     *Nx = TRUE;
 | |
|     if (IsInSmmRanges (Address)) {
 | |
|       *Nx = FALSE;
 | |
|     }
 | |
|     return TRUE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check if the memory address will be mapped by 4KB-page.
 | |
| 
 | |
|   @param  Address  The address of Memory.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| IsAddressSplit (
 | |
|   IN EFI_PHYSICAL_ADDRESS   Address
 | |
|   )
 | |
| {
 | |
|   UINTN  Index;
 | |
| 
 | |
|   if (FeaturePcdGet (PcdCpuSmmProfileEnable)) {
 | |
|     //
 | |
|     // Check configuration
 | |
|     //
 | |
|     for (Index = 0; Index < mSplitMemRangeCount; Index++) {
 | |
|       if ((Address >= mSplitMemRange[Index].Base) && (Address < mSplitMemRange[Index].Top)) {
 | |
|         return TRUE;
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     if (Address < mCpuHotPlugData.SmrrBase) {
 | |
|       if ((mCpuHotPlugData.SmrrBase - Address) < BASE_2MB) {
 | |
|         return TRUE;
 | |
|       }
 | |
|     } else if (Address > (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize - BASE_2MB))  {
 | |
|       if ((Address - (mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize - BASE_2MB)) < BASE_2MB) {
 | |
|         return TRUE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Return default
 | |
|   //
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize the protected memory ranges and the 4KB-page mapped memory ranges.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| InitProtectedMemRange (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   UINTN                            Index;
 | |
|   UINTN                            NumberOfDescriptors;
 | |
|   UINTN                            NumberOfAddedDescriptors;
 | |
|   UINTN                            NumberOfProtectRange;
 | |
|   UINTN                            NumberOfSpliteRange;
 | |
|   EFI_GCD_MEMORY_SPACE_DESCRIPTOR  *MemorySpaceMap;
 | |
|   UINTN                            TotalSize;
 | |
|   EFI_PHYSICAL_ADDRESS             ProtectBaseAddress;
 | |
|   EFI_PHYSICAL_ADDRESS             ProtectEndAddress;
 | |
|   EFI_PHYSICAL_ADDRESS             Top2MBAlignedAddress;
 | |
|   EFI_PHYSICAL_ADDRESS             Base2MBAlignedAddress;
 | |
|   UINT64                           High4KBPageSize;
 | |
|   UINT64                           Low4KBPageSize;
 | |
| 
 | |
|   NumberOfDescriptors      = 0;
 | |
|   NumberOfAddedDescriptors = mSmmCpuSmramRangeCount;
 | |
|   NumberOfSpliteRange      = 0;
 | |
|   MemorySpaceMap           = NULL;
 | |
| 
 | |
|   //
 | |
|   // Get MMIO ranges from GCD and add them into protected memory ranges.
 | |
|   //
 | |
|   gDS->GetMemorySpaceMap (
 | |
|        &NumberOfDescriptors,
 | |
|        &MemorySpaceMap
 | |
|        );
 | |
|   for (Index = 0; Index < NumberOfDescriptors; Index++) {
 | |
|     if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {
 | |
|       NumberOfAddedDescriptors++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (NumberOfAddedDescriptors != 0) {
 | |
|     TotalSize = NumberOfAddedDescriptors * sizeof (MEMORY_PROTECTION_RANGE) + sizeof (mProtectionMemRangeTemplate);
 | |
|     mProtectionMemRange = (MEMORY_PROTECTION_RANGE *) AllocateZeroPool (TotalSize);
 | |
|     ASSERT (mProtectionMemRange != NULL);
 | |
|     mProtectionMemRangeCount = TotalSize / sizeof (MEMORY_PROTECTION_RANGE);
 | |
| 
 | |
|     //
 | |
|     // Copy existing ranges.
 | |
|     //
 | |
|     CopyMem (mProtectionMemRange, mProtectionMemRangeTemplate, sizeof (mProtectionMemRangeTemplate));
 | |
| 
 | |
|     //
 | |
|     // Create split ranges which come from protected ranges.
 | |
|     //
 | |
|     TotalSize = (TotalSize / sizeof (MEMORY_PROTECTION_RANGE)) * sizeof (MEMORY_RANGE);
 | |
|     mSplitMemRange = (MEMORY_RANGE *) AllocateZeroPool (TotalSize);
 | |
|     ASSERT (mSplitMemRange != NULL);
 | |
| 
 | |
|     //
 | |
|     // Create SMM ranges which are set to present and execution-enable.
 | |
|     //
 | |
|     NumberOfProtectRange = sizeof (mProtectionMemRangeTemplate) / sizeof (MEMORY_PROTECTION_RANGE);
 | |
|     for (Index = 0; Index < mSmmCpuSmramRangeCount; Index++) {
 | |
|       if (mSmmCpuSmramRanges[Index].CpuStart >= mProtectionMemRange[0].Range.Base &&
 | |
|           mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize < mProtectionMemRange[0].Range.Top) {
 | |
|         //
 | |
|         // If the address have been already covered by mCpuHotPlugData.SmrrBase/mCpuHotPlugData.SmrrSiz
 | |
|         //
 | |
|         break;
 | |
|       }
 | |
|       mProtectionMemRange[NumberOfProtectRange].Range.Base = mSmmCpuSmramRanges[Index].CpuStart;
 | |
|       mProtectionMemRange[NumberOfProtectRange].Range.Top  = mSmmCpuSmramRanges[Index].CpuStart + mSmmCpuSmramRanges[Index].PhysicalSize;
 | |
|       mProtectionMemRange[NumberOfProtectRange].Present    = TRUE;
 | |
|       mProtectionMemRange[NumberOfProtectRange].Nx         = FALSE;
 | |
|       NumberOfProtectRange++;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Create MMIO ranges which are set to present and execution-disable.
 | |
|     //
 | |
|     for (Index = 0; Index < NumberOfDescriptors; Index++) {
 | |
|       if (MemorySpaceMap[Index].GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo) {
 | |
|         continue;
 | |
|       }
 | |
|       mProtectionMemRange[NumberOfProtectRange].Range.Base = MemorySpaceMap[Index].BaseAddress;
 | |
|       mProtectionMemRange[NumberOfProtectRange].Range.Top  = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length;
 | |
|       mProtectionMemRange[NumberOfProtectRange].Present    = TRUE;
 | |
|       mProtectionMemRange[NumberOfProtectRange].Nx         = TRUE;
 | |
|       NumberOfProtectRange++;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Check and updated actual protected memory ranges count
 | |
|     //
 | |
|     ASSERT (NumberOfProtectRange <= mProtectionMemRangeCount);
 | |
|     mProtectionMemRangeCount = NumberOfProtectRange;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // According to protected ranges, create the ranges which will be mapped by 2KB page.
 | |
|   //
 | |
|   NumberOfSpliteRange  = 0;
 | |
|   NumberOfProtectRange = mProtectionMemRangeCount;
 | |
|   for (Index = 0; Index < NumberOfProtectRange; Index++) {
 | |
|     //
 | |
|     // If MMIO base address is not 2MB alignment, make 2MB alignment for create 4KB page in page table.
 | |
|     //
 | |
|     ProtectBaseAddress = mProtectionMemRange[Index].Range.Base;
 | |
|     ProtectEndAddress  = mProtectionMemRange[Index].Range.Top;
 | |
|     if (((ProtectBaseAddress & (SIZE_2MB - 1)) != 0) || ((ProtectEndAddress  & (SIZE_2MB - 1)) != 0)) {
 | |
|       //
 | |
|       // Check if it is possible to create 4KB-page for not 2MB-aligned range and to create 2MB-page for 2MB-aligned range.
 | |
|       // A mix of 4KB and 2MB page could save SMRAM space.
 | |
|       //
 | |
|       Top2MBAlignedAddress  = ProtectEndAddress & ~(SIZE_2MB - 1);
 | |
|       Base2MBAlignedAddress = (ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
 | |
|       if ((Top2MBAlignedAddress > Base2MBAlignedAddress) &&
 | |
|           ((Top2MBAlignedAddress - Base2MBAlignedAddress) >= SIZE_2MB)) {
 | |
|         //
 | |
|         // There is an range which could be mapped by 2MB-page.
 | |
|         //
 | |
|         High4KBPageSize = ((ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) - (ProtectEndAddress & ~(SIZE_2MB - 1));
 | |
|         Low4KBPageSize  = ((ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1)) - (ProtectBaseAddress & ~(SIZE_2MB - 1));
 | |
|         if (High4KBPageSize != 0) {
 | |
|           //
 | |
|           // Add not 2MB-aligned range to be mapped by 4KB-page.
 | |
|           //
 | |
|           mSplitMemRange[NumberOfSpliteRange].Base = ProtectEndAddress & ~(SIZE_2MB - 1);
 | |
|           mSplitMemRange[NumberOfSpliteRange].Top  = (ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
 | |
|           NumberOfSpliteRange++;
 | |
|         }
 | |
|         if (Low4KBPageSize != 0) {
 | |
|           //
 | |
|           // Add not 2MB-aligned range to be mapped by 4KB-page.
 | |
|           //
 | |
|           mSplitMemRange[NumberOfSpliteRange].Base = ProtectBaseAddress & ~(SIZE_2MB - 1);
 | |
|           mSplitMemRange[NumberOfSpliteRange].Top  = (ProtectBaseAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
 | |
|           NumberOfSpliteRange++;
 | |
|         }
 | |
|       } else {
 | |
|         //
 | |
|         // The range could only be mapped by 4KB-page.
 | |
|         //
 | |
|         mSplitMemRange[NumberOfSpliteRange].Base = ProtectBaseAddress & ~(SIZE_2MB - 1);
 | |
|         mSplitMemRange[NumberOfSpliteRange].Top  = (ProtectEndAddress + SIZE_2MB - 1) & ~(SIZE_2MB - 1);
 | |
|         NumberOfSpliteRange++;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   mSplitMemRangeCount = NumberOfSpliteRange;
 | |
| 
 | |
|   DEBUG ((EFI_D_INFO, "SMM Profile Memory Ranges:\n"));
 | |
|   for (Index = 0; Index < mProtectionMemRangeCount; Index++) {
 | |
|     DEBUG ((EFI_D_INFO, "mProtectionMemRange[%d].Base = %lx\n", Index, mProtectionMemRange[Index].Range.Base));
 | |
|     DEBUG ((EFI_D_INFO, "mProtectionMemRange[%d].Top  = %lx\n", Index, mProtectionMemRange[Index].Range.Top));
 | |
|   }
 | |
|   for (Index = 0; Index < mSplitMemRangeCount; Index++) {
 | |
|     DEBUG ((EFI_D_INFO, "mSplitMemRange[%d].Base = %lx\n", Index, mSplitMemRange[Index].Base));
 | |
|     DEBUG ((EFI_D_INFO, "mSplitMemRange[%d].Top  = %lx\n", Index, mSplitMemRange[Index].Top));
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Update page table according to protected memory ranges and the 4KB-page mapped memory ranges.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| InitPaging (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   UINT64                            Pml5Entry;
 | |
|   UINT64                            Pml4Entry;
 | |
|   UINT64                            *Pml5;
 | |
|   UINT64                            *Pml4;
 | |
|   UINT64                            *Pdpt;
 | |
|   UINT64                            *Pd;
 | |
|   UINT64                            *Pt;
 | |
|   UINTN                             Address;
 | |
|   UINTN                             Pml5Index;
 | |
|   UINTN                             Pml4Index;
 | |
|   UINTN                             PdptIndex;
 | |
|   UINTN                             PdIndex;
 | |
|   UINTN                             PtIndex;
 | |
|   UINTN                             NumberOfPdptEntries;
 | |
|   UINTN                             NumberOfPml4Entries;
 | |
|   UINTN                             NumberOfPml5Entries;
 | |
|   UINTN                             SizeOfMemorySpace;
 | |
|   BOOLEAN                           Nx;
 | |
|   IA32_CR4                          Cr4;
 | |
|   BOOLEAN                           Enable5LevelPaging;
 | |
| 
 | |
|   Cr4.UintN = AsmReadCr4 ();
 | |
|   Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
 | |
| 
 | |
|   if (sizeof (UINTN) == sizeof (UINT64)) {
 | |
|     if (!Enable5LevelPaging) {
 | |
|       Pml5Entry = (UINTN) mSmmProfileCr3 | IA32_PG_P;
 | |
|       Pml5 = &Pml5Entry;
 | |
|     } else {
 | |
|       Pml5 = (UINT64*) (UINTN) mSmmProfileCr3;
 | |
|     }
 | |
|     SizeOfMemorySpace = HighBitSet64 (gPhyMask) + 1;
 | |
|     //
 | |
|     // Calculate the table entries of PML4E and PDPTE.
 | |
|     //
 | |
|     NumberOfPml5Entries = 1;
 | |
|     if (SizeOfMemorySpace > 48) {
 | |
|       NumberOfPml5Entries = (UINTN) LShiftU64 (1, SizeOfMemorySpace - 48);
 | |
|       SizeOfMemorySpace = 48;
 | |
|     }
 | |
| 
 | |
|     NumberOfPml4Entries = 1;
 | |
|     if (SizeOfMemorySpace > 39) {
 | |
|       NumberOfPml4Entries = (UINTN) LShiftU64 (1, SizeOfMemorySpace - 39);
 | |
|       SizeOfMemorySpace = 39;
 | |
|     }
 | |
| 
 | |
|     NumberOfPdptEntries = 1;
 | |
|     ASSERT (SizeOfMemorySpace > 30);
 | |
|     NumberOfPdptEntries = (UINTN) LShiftU64 (1, SizeOfMemorySpace - 30);
 | |
|   } else {
 | |
|     Pml4Entry = (UINTN) mSmmProfileCr3 | IA32_PG_P;
 | |
|     Pml4 = &Pml4Entry;
 | |
|     Pml5Entry = (UINTN) Pml4 | IA32_PG_P;
 | |
|     Pml5 = &Pml5Entry;
 | |
|     NumberOfPml5Entries  = 1;
 | |
|     NumberOfPml4Entries  = 1;
 | |
|     NumberOfPdptEntries  = 4;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Go through page table and change 2MB-page into 4KB-page.
 | |
|   //
 | |
|   for (Pml5Index = 0; Pml5Index < NumberOfPml5Entries; Pml5Index++) {
 | |
|     if ((Pml5[Pml5Index] & IA32_PG_P) == 0) {
 | |
|       //
 | |
|       // If PML5 entry does not exist, skip it
 | |
|       //
 | |
|       continue;
 | |
|     }
 | |
|     Pml4 = (UINT64 *) (UINTN) (Pml5[Pml5Index] & PHYSICAL_ADDRESS_MASK);
 | |
|     for (Pml4Index = 0; Pml4Index < NumberOfPml4Entries; Pml4Index++) {
 | |
|       if ((Pml4[Pml4Index] & IA32_PG_P) == 0) {
 | |
|         //
 | |
|         // If PML4 entry does not exist, skip it
 | |
|         //
 | |
|         continue;
 | |
|       }
 | |
|       Pdpt = (UINT64 *)(UINTN)(Pml4[Pml4Index] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);
 | |
|       for (PdptIndex = 0; PdptIndex < NumberOfPdptEntries; PdptIndex++, Pdpt++) {
 | |
|         if ((*Pdpt & IA32_PG_P) == 0) {
 | |
|           //
 | |
|           // If PDPT entry does not exist, skip it
 | |
|           //
 | |
|           continue;
 | |
|         }
 | |
|         if ((*Pdpt & IA32_PG_PS) != 0) {
 | |
|           //
 | |
|           // This is 1G entry, skip it
 | |
|           //
 | |
|           continue;
 | |
|         }
 | |
|         Pd = (UINT64 *)(UINTN)(*Pdpt & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);
 | |
|         if (Pd == 0) {
 | |
|           continue;
 | |
|         }
 | |
|         for (PdIndex = 0; PdIndex < SIZE_4KB / sizeof (*Pd); PdIndex++, Pd++) {
 | |
|           if ((*Pd & IA32_PG_P) == 0) {
 | |
|             //
 | |
|             // If PD entry does not exist, skip it
 | |
|             //
 | |
|             continue;
 | |
|           }
 | |
|           Address = (UINTN) LShiftU64 (
 | |
|                               LShiftU64 (
 | |
|                                 LShiftU64 ((Pml5Index << 9) + Pml4Index, 9) + PdptIndex,
 | |
|                                 9
 | |
|                                 ) + PdIndex,
 | |
|                                 21
 | |
|                               );
 | |
| 
 | |
|           //
 | |
|           // If it is 2M page, check IsAddressSplit()
 | |
|           //
 | |
|           if (((*Pd & IA32_PG_PS) != 0) && IsAddressSplit (Address)) {
 | |
|             //
 | |
|             // Based on current page table, create 4KB page table for split area.
 | |
|             //
 | |
|             ASSERT (Address == (*Pd & PHYSICAL_ADDRESS_MASK));
 | |
| 
 | |
|             Pt = AllocatePageTableMemory (1);
 | |
|             ASSERT (Pt != NULL);
 | |
| 
 | |
|             // Split it
 | |
|             for (PtIndex = 0; PtIndex < SIZE_4KB / sizeof(*Pt); PtIndex++) {
 | |
|               Pt[PtIndex] = Address + ((PtIndex << 12) | mAddressEncMask | PAGE_ATTRIBUTE_BITS);
 | |
|             } // end for PT
 | |
|             *Pd = (UINT64)(UINTN)Pt | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
 | |
|           } // end if IsAddressSplit
 | |
|         } // end for PD
 | |
|       } // end for PDPT
 | |
|     } // end for PML4
 | |
|   } // end for PML5
 | |
| 
 | |
|   //
 | |
|   // Go through page table and set several page table entries to absent or execute-disable.
 | |
|   //
 | |
|   DEBUG ((EFI_D_INFO, "Patch page table start ...\n"));
 | |
|   for (Pml5Index = 0; Pml5Index < NumberOfPml5Entries; Pml5Index++) {
 | |
|     if ((Pml5[Pml5Index] & IA32_PG_P) == 0) {
 | |
|       //
 | |
|       // If PML5 entry does not exist, skip it
 | |
|       //
 | |
|       continue;
 | |
|     }
 | |
|     Pml4 = (UINT64 *) (UINTN) (Pml5[Pml5Index] & PHYSICAL_ADDRESS_MASK);
 | |
|     for (Pml4Index = 0; Pml4Index < NumberOfPml4Entries; Pml4Index++) {
 | |
|       if ((Pml4[Pml4Index] & IA32_PG_P) == 0) {
 | |
|         //
 | |
|         // If PML4 entry does not exist, skip it
 | |
|         //
 | |
|         continue;
 | |
|       }
 | |
|       Pdpt = (UINT64 *)(UINTN)(Pml4[Pml4Index] & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);
 | |
|       for (PdptIndex = 0; PdptIndex < NumberOfPdptEntries; PdptIndex++, Pdpt++) {
 | |
|         if ((*Pdpt & IA32_PG_P) == 0) {
 | |
|           //
 | |
|           // If PDPT entry does not exist, skip it
 | |
|           //
 | |
|           continue;
 | |
|         }
 | |
|         if ((*Pdpt & IA32_PG_PS) != 0) {
 | |
|           //
 | |
|           // This is 1G entry, set NX bit and skip it
 | |
|           //
 | |
|           if (mXdSupported) {
 | |
|             *Pdpt = *Pdpt | IA32_PG_NX;
 | |
|           }
 | |
|           continue;
 | |
|         }
 | |
|         Pd = (UINT64 *)(UINTN)(*Pdpt & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);
 | |
|         if (Pd == 0) {
 | |
|           continue;
 | |
|         }
 | |
|         for (PdIndex = 0; PdIndex < SIZE_4KB / sizeof (*Pd); PdIndex++, Pd++) {
 | |
|           if ((*Pd & IA32_PG_P) == 0) {
 | |
|             //
 | |
|             // If PD entry does not exist, skip it
 | |
|             //
 | |
|             continue;
 | |
|           }
 | |
|           Address = (UINTN) LShiftU64 (
 | |
|                               LShiftU64 (
 | |
|                                 LShiftU64 ((Pml5Index << 9) + Pml4Index, 9) + PdptIndex,
 | |
|                                 9
 | |
|                                 ) + PdIndex,
 | |
|                                 21
 | |
|                               );
 | |
| 
 | |
|           if ((*Pd & IA32_PG_PS) != 0) {
 | |
|             // 2MB page
 | |
| 
 | |
|             if (!IsAddressValid (Address, &Nx)) {
 | |
|               //
 | |
|               // Patch to remove Present flag and RW flag
 | |
|               //
 | |
|               *Pd = *Pd & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS);
 | |
|             }
 | |
|             if (Nx && mXdSupported) {
 | |
|               *Pd = *Pd | IA32_PG_NX;
 | |
|             }
 | |
|           } else {
 | |
|             // 4KB page
 | |
|             Pt = (UINT64 *)(UINTN)(*Pd & ~mAddressEncMask & PHYSICAL_ADDRESS_MASK);
 | |
|             if (Pt == 0) {
 | |
|               continue;
 | |
|             }
 | |
|             for (PtIndex = 0; PtIndex < SIZE_4KB / sizeof(*Pt); PtIndex++, Pt++) {
 | |
|               if (!IsAddressValid (Address, &Nx)) {
 | |
|                 *Pt = *Pt & (INTN)(INT32)(~PAGE_ATTRIBUTE_BITS);
 | |
|               }
 | |
|               if (Nx && mXdSupported) {
 | |
|                 *Pt = *Pt | IA32_PG_NX;
 | |
|               }
 | |
|               Address += SIZE_4KB;
 | |
|             } // end for PT
 | |
|           } // end if PS
 | |
|         } // end for PD
 | |
|       } // end for PDPT
 | |
|     } // end for PML4
 | |
|   } // end for PML5
 | |
| 
 | |
|   //
 | |
|   // Flush TLB
 | |
|   //
 | |
|   CpuFlushTlb ();
 | |
|   DEBUG ((EFI_D_INFO, "Patch page table done!\n"));
 | |
|   //
 | |
|   // Set execute-disable flag
 | |
|   //
 | |
|   mXdEnabled = TRUE;
 | |
| 
 | |
|   return ;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   To get system port address of the SMI Command Port in FADT table.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| GetSmiCommandPort (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *Fadt;
 | |
| 
 | |
|   Fadt = (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE *) EfiLocateFirstAcpiTable (
 | |
|                                                          EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
 | |
|                                                          );
 | |
|   ASSERT (Fadt != NULL);
 | |
| 
 | |
|   mSmiCommandPort = Fadt->SmiCmd;
 | |
|   DEBUG ((EFI_D_INFO, "mSmiCommandPort = %x\n", mSmiCommandPort));
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Updates page table to make some memory ranges (like system memory) absent
 | |
|   and make some memory ranges (like MMIO) present and execute disable. It also
 | |
|   update 2MB-page to 4KB-page for some memory ranges.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| SmmProfileStart (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // The flag indicates SMM profile starts to work.
 | |
|   //
 | |
|   mSmmProfileStart = TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize SMM profile in SmmReadyToLock protocol callback function.
 | |
| 
 | |
|   @param  Protocol   Points to the protocol's unique identifier.
 | |
|   @param  Interface  Points to the interface instance.
 | |
|   @param  Handle     The handle on which the interface was installed.
 | |
| 
 | |
|   @retval EFI_SUCCESS SmmReadyToLock protocol callback runs successfully.
 | |
| **/
 | |
| EFI_STATUS
 | |
| EFIAPI
 | |
| InitSmmProfileCallBack (
 | |
|   IN CONST EFI_GUID  *Protocol,
 | |
|   IN VOID            *Interface,
 | |
|   IN EFI_HANDLE      Handle
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Save to variable so that SMM profile data can be found.
 | |
|   //
 | |
|   gRT->SetVariable (
 | |
|          SMM_PROFILE_NAME,
 | |
|          &gEfiCallerIdGuid,
 | |
|          EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
 | |
|          sizeof(mSmmProfileBase),
 | |
|          &mSmmProfileBase
 | |
|          );
 | |
| 
 | |
|   //
 | |
|   // Get Software SMI from FADT
 | |
|   //
 | |
|   GetSmiCommandPort ();
 | |
| 
 | |
|   //
 | |
|   // Initialize protected memory range for patching page table later.
 | |
|   //
 | |
|   InitProtectedMemRange ();
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize SMM profile data structures.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| InitSmmProfileInternal (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                 Status;
 | |
|   EFI_PHYSICAL_ADDRESS       Base;
 | |
|   VOID                       *Registration;
 | |
|   UINTN                      Index;
 | |
|   UINTN                      MsrDsAreaSizePerCpu;
 | |
|   UINTN                      TotalSize;
 | |
| 
 | |
|   mPFEntryCount = (UINTN *)AllocateZeroPool (sizeof (UINTN) * mMaxNumberOfCpus);
 | |
|   ASSERT (mPFEntryCount != NULL);
 | |
|   mLastPFEntryValue = (UINT64  (*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool (
 | |
|                                                          sizeof (mLastPFEntryValue[0]) * mMaxNumberOfCpus);
 | |
|   ASSERT (mLastPFEntryValue != NULL);
 | |
|   mLastPFEntryPointer = (UINT64 *(*)[MAX_PF_ENTRY_COUNT])AllocateZeroPool (
 | |
|                                                            sizeof (mLastPFEntryPointer[0]) * mMaxNumberOfCpus);
 | |
|   ASSERT (mLastPFEntryPointer != NULL);
 | |
| 
 | |
|   //
 | |
|   // Allocate memory for SmmProfile below 4GB.
 | |
|   // The base address
 | |
|   //
 | |
|   mSmmProfileSize = PcdGet32 (PcdCpuSmmProfileSize);
 | |
|   ASSERT ((mSmmProfileSize & 0xFFF) == 0);
 | |
| 
 | |
|   if (mBtsSupported) {
 | |
|     TotalSize = mSmmProfileSize + mMsrDsAreaSize;
 | |
|   } else {
 | |
|     TotalSize = mSmmProfileSize;
 | |
|   }
 | |
| 
 | |
|   Base = 0xFFFFFFFF;
 | |
|   Status = gBS->AllocatePages (
 | |
|                   AllocateMaxAddress,
 | |
|                   EfiReservedMemoryType,
 | |
|                   EFI_SIZE_TO_PAGES (TotalSize),
 | |
|                   &Base
 | |
|                   );
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
|   ZeroMem ((VOID *)(UINTN)Base, TotalSize);
 | |
|   mSmmProfileBase = (SMM_PROFILE_HEADER *)(UINTN)Base;
 | |
| 
 | |
|   //
 | |
|   // Initialize SMM profile data header.
 | |
|   //
 | |
|   mSmmProfileBase->HeaderSize     = sizeof (SMM_PROFILE_HEADER);
 | |
|   mSmmProfileBase->MaxDataEntries = (UINT64)((mSmmProfileSize - sizeof(SMM_PROFILE_HEADER)) / sizeof (SMM_PROFILE_ENTRY));
 | |
|   mSmmProfileBase->MaxDataSize    = MultU64x64 (mSmmProfileBase->MaxDataEntries, sizeof(SMM_PROFILE_ENTRY));
 | |
|   mSmmProfileBase->CurDataEntries = 0;
 | |
|   mSmmProfileBase->CurDataSize    = 0;
 | |
|   mSmmProfileBase->TsegStart      = mCpuHotPlugData.SmrrBase;
 | |
|   mSmmProfileBase->TsegSize       = mCpuHotPlugData.SmrrSize;
 | |
|   mSmmProfileBase->NumSmis        = 0;
 | |
|   mSmmProfileBase->NumCpus        = gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus;
 | |
| 
 | |
|   if (mBtsSupported) {
 | |
|     mMsrDsArea = (MSR_DS_AREA_STRUCT **)AllocateZeroPool (sizeof (MSR_DS_AREA_STRUCT *) * mMaxNumberOfCpus);
 | |
|     ASSERT (mMsrDsArea != NULL);
 | |
|     mMsrBTSRecord = (BRANCH_TRACE_RECORD **)AllocateZeroPool (sizeof (BRANCH_TRACE_RECORD *) * mMaxNumberOfCpus);
 | |
|     ASSERT (mMsrBTSRecord != NULL);
 | |
|     mMsrPEBSRecord = (PEBS_RECORD **)AllocateZeroPool (sizeof (PEBS_RECORD *) * mMaxNumberOfCpus);
 | |
|     ASSERT (mMsrPEBSRecord != NULL);
 | |
| 
 | |
|     mMsrDsAreaBase  = (MSR_DS_AREA_STRUCT *)((UINTN)Base + mSmmProfileSize);
 | |
|     MsrDsAreaSizePerCpu = mMsrDsAreaSize / mMaxNumberOfCpus;
 | |
|     mBTSRecordNumber    = (MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER - sizeof(MSR_DS_AREA_STRUCT)) / sizeof(BRANCH_TRACE_RECORD);
 | |
|     for (Index = 0; Index < mMaxNumberOfCpus; Index++) {
 | |
|       mMsrDsArea[Index]     = (MSR_DS_AREA_STRUCT *)((UINTN)mMsrDsAreaBase + MsrDsAreaSizePerCpu * Index);
 | |
|       mMsrBTSRecord[Index]  = (BRANCH_TRACE_RECORD *)((UINTN)mMsrDsArea[Index] + sizeof(MSR_DS_AREA_STRUCT));
 | |
|       mMsrPEBSRecord[Index] = (PEBS_RECORD *)((UINTN)mMsrDsArea[Index] + MsrDsAreaSizePerCpu - sizeof(PEBS_RECORD) * PEBS_RECORD_NUMBER);
 | |
| 
 | |
|       mMsrDsArea[Index]->BTSBufferBase          = (UINTN)mMsrBTSRecord[Index];
 | |
|       mMsrDsArea[Index]->BTSIndex               = mMsrDsArea[Index]->BTSBufferBase;
 | |
|       mMsrDsArea[Index]->BTSAbsoluteMaximum     = mMsrDsArea[Index]->BTSBufferBase + mBTSRecordNumber * sizeof(BRANCH_TRACE_RECORD) + 1;
 | |
|       mMsrDsArea[Index]->BTSInterruptThreshold  = mMsrDsArea[Index]->BTSAbsoluteMaximum + 1;
 | |
| 
 | |
|       mMsrDsArea[Index]->PEBSBufferBase         = (UINTN)mMsrPEBSRecord[Index];
 | |
|       mMsrDsArea[Index]->PEBSIndex              = mMsrDsArea[Index]->PEBSBufferBase;
 | |
|       mMsrDsArea[Index]->PEBSAbsoluteMaximum    = mMsrDsArea[Index]->PEBSBufferBase + PEBS_RECORD_NUMBER * sizeof(PEBS_RECORD) + 1;
 | |
|       mMsrDsArea[Index]->PEBSInterruptThreshold = mMsrDsArea[Index]->PEBSAbsoluteMaximum + 1;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   mProtectionMemRange      = mProtectionMemRangeTemplate;
 | |
|   mProtectionMemRangeCount = sizeof (mProtectionMemRangeTemplate) / sizeof (MEMORY_PROTECTION_RANGE);
 | |
| 
 | |
|   //
 | |
|   // Update TSeg entry.
 | |
|   //
 | |
|   mProtectionMemRange[0].Range.Base = mCpuHotPlugData.SmrrBase;
 | |
|   mProtectionMemRange[0].Range.Top  = mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize;
 | |
| 
 | |
|   //
 | |
|   // Update SMM profile entry.
 | |
|   //
 | |
|   mProtectionMemRange[1].Range.Base = (EFI_PHYSICAL_ADDRESS)(UINTN)mSmmProfileBase;
 | |
|   mProtectionMemRange[1].Range.Top  = (EFI_PHYSICAL_ADDRESS)(UINTN)mSmmProfileBase + TotalSize;
 | |
| 
 | |
|   //
 | |
|   // Allocate memory reserved for creating 4KB pages.
 | |
|   //
 | |
|   InitPagesForPFHandler ();
 | |
| 
 | |
|   //
 | |
|   // Start SMM profile when SmmReadyToLock protocol is installed.
 | |
|   //
 | |
|   Status = gSmst->SmmRegisterProtocolNotify (
 | |
|                     &gEfiSmmReadyToLockProtocolGuid,
 | |
|                     InitSmmProfileCallBack,
 | |
|                     &Registration
 | |
|                     );
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|   return ;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Check if feature is supported by a processor.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| CheckFeatureSupported (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   UINT32                         RegEax;
 | |
|   UINT32                         RegEcx;
 | |
|   UINT32                         RegEdx;
 | |
|   MSR_IA32_MISC_ENABLE_REGISTER  MiscEnableMsr;
 | |
| 
 | |
|   if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) {
 | |
|     AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
 | |
|     if (RegEax <= CPUID_EXTENDED_FUNCTION) {
 | |
|       mCetSupported = FALSE;
 | |
|       PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1);
 | |
|     }
 | |
|     AsmCpuidEx (CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS, CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO, NULL, NULL, &RegEcx, NULL);
 | |
|     if ((RegEcx & CPUID_CET_SS) == 0) {
 | |
|       mCetSupported = FALSE;
 | |
|       PatchInstructionX86 (mPatchCetSupported, mCetSupported, 1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (mXdSupported) {
 | |
|     AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL);
 | |
|     if (RegEax <= CPUID_EXTENDED_FUNCTION) {
 | |
|       //
 | |
|       // Extended CPUID functions are not supported on this processor.
 | |
|       //
 | |
|       mXdSupported = FALSE;
 | |
|       PatchInstructionX86 (gPatchXdSupported, mXdSupported, 1);
 | |
|     }
 | |
| 
 | |
|     AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx);
 | |
|     if ((RegEdx & CPUID1_EDX_XD_SUPPORT) == 0) {
 | |
|       //
 | |
|       // Execute Disable Bit feature is not supported on this processor.
 | |
|       //
 | |
|       mXdSupported = FALSE;
 | |
|       PatchInstructionX86 (gPatchXdSupported, mXdSupported, 1);
 | |
|     }
 | |
| 
 | |
|     if (StandardSignatureIsAuthenticAMD ()) {
 | |
|       //
 | |
|       // AMD processors do not support MSR_IA32_MISC_ENABLE
 | |
|       //
 | |
|       PatchInstructionX86 (gPatchMsrIa32MiscEnableSupported, FALSE, 1);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (mBtsSupported) {
 | |
|     AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, NULL, &RegEdx);
 | |
|     if ((RegEdx & CPUID1_EDX_BTS_AVAILABLE) != 0) {
 | |
|       //
 | |
|       // Per IA32 manuals:
 | |
|       // When CPUID.1:EDX[21] is set, the following BTS facilities are available:
 | |
|       // 1. The BTS_UNAVAILABLE flag in the IA32_MISC_ENABLE MSR indicates the
 | |
|       //    availability of the BTS facilities, including the ability to set the BTS and
 | |
|       //    BTINT bits in the MSR_DEBUGCTLA MSR.
 | |
|       // 2. The IA32_DS_AREA MSR can be programmed to point to the DS save area.
 | |
|       //
 | |
|       MiscEnableMsr.Uint64 = AsmReadMsr64 (MSR_IA32_MISC_ENABLE);
 | |
|       if (MiscEnableMsr.Bits.BTS == 1) {
 | |
|         //
 | |
|         // BTS facilities is not supported if MSR_IA32_MISC_ENABLE.BTS bit is set.
 | |
|         //
 | |
|         mBtsSupported = FALSE;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable single step.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ActivateSingleStepDB (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   UINTN    Dr6;
 | |
| 
 | |
|   Dr6 = AsmReadDr6 ();
 | |
|   if ((Dr6 & DR6_SINGLE_STEP) != 0) {
 | |
|     return;
 | |
|   }
 | |
|   Dr6 |= DR6_SINGLE_STEP;
 | |
|   AsmWriteDr6 (Dr6);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable last branch.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ActivateLBR (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   UINT64  DebugCtl;
 | |
| 
 | |
|   DebugCtl = AsmReadMsr64 (MSR_DEBUG_CTL);
 | |
|   if ((DebugCtl & MSR_DEBUG_CTL_LBR) != 0) {
 | |
|     return ;
 | |
|   }
 | |
|   DebugCtl |= MSR_DEBUG_CTL_LBR;
 | |
|   AsmWriteMsr64 (MSR_DEBUG_CTL, DebugCtl);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Enable branch trace store.
 | |
| 
 | |
|   @param  CpuIndex  The index of the processor.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ActivateBTS (
 | |
|   IN      UINTN                     CpuIndex
 | |
|   )
 | |
| {
 | |
|   UINT64  DebugCtl;
 | |
| 
 | |
|   DebugCtl = AsmReadMsr64 (MSR_DEBUG_CTL);
 | |
|   if ((DebugCtl & MSR_DEBUG_CTL_BTS) != 0) {
 | |
|     return ;
 | |
|   }
 | |
| 
 | |
|   AsmWriteMsr64 (MSR_DS_AREA, (UINT64)(UINTN)mMsrDsArea[CpuIndex]);
 | |
|   DebugCtl |= (UINT64)(MSR_DEBUG_CTL_BTS | MSR_DEBUG_CTL_TR);
 | |
|   DebugCtl &= ~((UINT64)MSR_DEBUG_CTL_BTINT);
 | |
|   AsmWriteMsr64 (MSR_DEBUG_CTL, DebugCtl);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Increase SMI number in each SMI entry.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| SmmProfileRecordSmiNum (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   if (mSmmProfileStart) {
 | |
|     mSmmProfileBase->NumSmis++;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize processor environment for SMM profile.
 | |
| 
 | |
|   @param  CpuIndex  The index of the processor.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| ActivateSmmProfile (
 | |
|   IN UINTN CpuIndex
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Enable Single Step DB#
 | |
|   //
 | |
|   ActivateSingleStepDB ();
 | |
| 
 | |
|   if (mBtsSupported) {
 | |
|     //
 | |
|     // We can not get useful information from LER, so we have to use BTS.
 | |
|     //
 | |
|     ActivateLBR ();
 | |
| 
 | |
|     //
 | |
|     // Enable BTS
 | |
|     //
 | |
|     ActivateBTS (CpuIndex);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Initialize SMM profile in SMM CPU entry point.
 | |
| 
 | |
|   @param[in] Cr3  The base address of the page tables to use in SMM.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| InitSmmProfile (
 | |
|   UINT32  Cr3
 | |
|   )
 | |
| {
 | |
|   //
 | |
|   // Save Cr3
 | |
|   //
 | |
|   mSmmProfileCr3 = Cr3;
 | |
| 
 | |
|   //
 | |
|   // Skip SMM profile initialization if feature is disabled
 | |
|   //
 | |
|   if (!FeaturePcdGet (PcdCpuSmmProfileEnable) &&
 | |
|       !HEAP_GUARD_NONSTOP_MODE &&
 | |
|       !NULL_DETECTION_NONSTOP_MODE) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Initialize SmmProfile here
 | |
|   //
 | |
|   InitSmmProfileInternal ();
 | |
| 
 | |
|   //
 | |
|   // Initialize profile IDT.
 | |
|   //
 | |
|   InitIdtr ();
 | |
| 
 | |
|   //
 | |
|   // Tell #PF handler to prepare a #DB subsequently.
 | |
|   //
 | |
|   mSetupDebugTrap = TRUE;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Update page table to map the memory correctly in order to make the instruction
 | |
|   which caused page fault execute successfully. And it also save the original page
 | |
|   table to be restored in single-step exception.
 | |
| 
 | |
|   @param  PageTable           PageTable Address.
 | |
|   @param  PFAddress           The memory address which caused page fault exception.
 | |
|   @param  CpuIndex            The index of the processor.
 | |
|   @param  ErrorCode           The Error code of exception.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| RestorePageTableBelow4G (
 | |
|   UINT64        *PageTable,
 | |
|   UINT64        PFAddress,
 | |
|   UINTN         CpuIndex,
 | |
|   UINTN         ErrorCode
 | |
|   )
 | |
| {
 | |
|   UINTN         PTIndex;
 | |
|   UINTN         PFIndex;
 | |
|   IA32_CR4      Cr4;
 | |
|   BOOLEAN       Enable5LevelPaging;
 | |
| 
 | |
|   Cr4.UintN = AsmReadCr4 ();
 | |
|   Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
 | |
| 
 | |
|   //
 | |
|   // PML5
 | |
|   //
 | |
|   if (Enable5LevelPaging) {
 | |
|     PTIndex = (UINTN)BitFieldRead64 (PFAddress, 48, 56);
 | |
|     ASSERT (PageTable[PTIndex] != 0);
 | |
|     PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // PML4
 | |
|   //
 | |
|   if (sizeof(UINT64) == sizeof(UINTN)) {
 | |
|     PTIndex = (UINTN)BitFieldRead64 (PFAddress, 39, 47);
 | |
|     ASSERT (PageTable[PTIndex] != 0);
 | |
|     PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // PDPTE
 | |
|   //
 | |
|   PTIndex = (UINTN)BitFieldRead64 (PFAddress, 30, 38);
 | |
|   ASSERT (PageTable[PTIndex] != 0);
 | |
|   PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
 | |
| 
 | |
|   //
 | |
|   // PD
 | |
|   //
 | |
|   PTIndex = (UINTN)BitFieldRead64 (PFAddress, 21, 29);
 | |
|   if ((PageTable[PTIndex] & IA32_PG_PS) != 0) {
 | |
|     //
 | |
|     // Large page
 | |
|     //
 | |
| 
 | |
|     //
 | |
|     // Record old entries with non-present status
 | |
|     // Old entries include the memory which instruction is at and the memory which instruction access.
 | |
|     //
 | |
|     //
 | |
|     ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT);
 | |
|     if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) {
 | |
|       PFIndex = mPFEntryCount[CpuIndex];
 | |
|       mLastPFEntryValue[CpuIndex][PFIndex]   = PageTable[PTIndex];
 | |
|       mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex];
 | |
|       mPFEntryCount[CpuIndex]++;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Set new entry
 | |
|     //
 | |
|     PageTable[PTIndex] = (PFAddress & ~((1ull << 21) - 1));
 | |
|     PageTable[PTIndex] |= (UINT64)IA32_PG_PS;
 | |
|     PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS;
 | |
|     if ((ErrorCode & IA32_PF_EC_ID) != 0) {
 | |
|       PageTable[PTIndex] &= ~IA32_PG_NX;
 | |
|     }
 | |
|   } else {
 | |
|     //
 | |
|     // Small page
 | |
|     //
 | |
|     ASSERT (PageTable[PTIndex] != 0);
 | |
|     PageTable = (UINT64*)(UINTN)(PageTable[PTIndex] & PHYSICAL_ADDRESS_MASK);
 | |
| 
 | |
|     //
 | |
|     // 4K PTE
 | |
|     //
 | |
|     PTIndex = (UINTN)BitFieldRead64 (PFAddress, 12, 20);
 | |
| 
 | |
|     //
 | |
|     // Record old entries with non-present status
 | |
|     // Old entries include the memory which instruction is at and the memory which instruction access.
 | |
|     //
 | |
|     //
 | |
|     ASSERT (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT);
 | |
|     if (mPFEntryCount[CpuIndex] < MAX_PF_ENTRY_COUNT) {
 | |
|       PFIndex = mPFEntryCount[CpuIndex];
 | |
|       mLastPFEntryValue[CpuIndex][PFIndex]   = PageTable[PTIndex];
 | |
|       mLastPFEntryPointer[CpuIndex][PFIndex] = &PageTable[PTIndex];
 | |
|       mPFEntryCount[CpuIndex]++;
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Set new entry
 | |
|     //
 | |
|     PageTable[PTIndex] = (PFAddress & ~((1ull << 12) - 1));
 | |
|     PageTable[PTIndex] |= (UINT64)PAGE_ATTRIBUTE_BITS;
 | |
|     if ((ErrorCode & IA32_PF_EC_ID) != 0) {
 | |
|       PageTable[PTIndex] &= ~IA32_PG_NX;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Handler for Page Fault triggered by Guard page.
 | |
| 
 | |
|   @param  ErrorCode  The Error code of exception.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| GuardPagePFHandler (
 | |
|   UINTN ErrorCode
 | |
|   )
 | |
| {
 | |
|   UINT64                *PageTable;
 | |
|   UINT64                PFAddress;
 | |
|   UINT64                RestoreAddress;
 | |
|   UINTN                 RestorePageNumber;
 | |
|   UINTN                 CpuIndex;
 | |
| 
 | |
|   PageTable         = (UINT64 *)AsmReadCr3 ();
 | |
|   PFAddress         = AsmReadCr2 ();
 | |
|   CpuIndex          = GetCpuIndex ();
 | |
| 
 | |
|   //
 | |
|   // Memory operation cross pages, like "rep mov" instruction, will cause
 | |
|   // infinite loop between this and Debug Trap handler. We have to make sure
 | |
|   // that current page and the page followed are both in PRESENT state.
 | |
|   //
 | |
|   RestorePageNumber = 2;
 | |
|   RestoreAddress = PFAddress;
 | |
|   while (RestorePageNumber > 0) {
 | |
|     RestorePageTableBelow4G (PageTable, RestoreAddress, CpuIndex, ErrorCode);
 | |
|     RestoreAddress += EFI_PAGE_SIZE;
 | |
|     RestorePageNumber--;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Flush TLB
 | |
|   //
 | |
|   CpuFlushTlb ();
 | |
| }
 | |
| 
 | |
| /**
 | |
|   The Page fault handler to save SMM profile data.
 | |
| 
 | |
|   @param  Rip        The RIP when exception happens.
 | |
|   @param  ErrorCode  The Error code of exception.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| SmmProfilePFHandler (
 | |
|   UINTN Rip,
 | |
|   UINTN ErrorCode
 | |
|   )
 | |
| {
 | |
|   UINT64                *PageTable;
 | |
|   UINT64                PFAddress;
 | |
|   UINT64                RestoreAddress;
 | |
|   UINTN                 RestorePageNumber;
 | |
|   UINTN                 CpuIndex;
 | |
|   UINTN                 Index;
 | |
|   UINT64                InstructionAddress;
 | |
|   UINTN                 MaxEntryNumber;
 | |
|   UINTN                 CurrentEntryNumber;
 | |
|   BOOLEAN               IsValidPFAddress;
 | |
|   SMM_PROFILE_ENTRY     *SmmProfileEntry;
 | |
|   UINT64                SmiCommand;
 | |
|   EFI_STATUS            Status;
 | |
|   UINT8                 SoftSmiValue;
 | |
|   EFI_SMM_SAVE_STATE_IO_INFO    IoInfo;
 | |
| 
 | |
|   if (!mSmmProfileStart) {
 | |
|     //
 | |
|     // If SMM profile does not start, call original page fault handler.
 | |
|     //
 | |
|     SmiDefaultPFHandler ();
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (mBtsSupported) {
 | |
|     DisableBTS ();
 | |
|   }
 | |
| 
 | |
|   IsValidPFAddress  = FALSE;
 | |
|   PageTable         = (UINT64 *)AsmReadCr3 ();
 | |
|   PFAddress         = AsmReadCr2 ();
 | |
|   CpuIndex          = GetCpuIndex ();
 | |
| 
 | |
|   //
 | |
|   // Memory operation cross pages, like "rep mov" instruction, will cause
 | |
|   // infinite loop between this and Debug Trap handler. We have to make sure
 | |
|   // that current page and the page followed are both in PRESENT state.
 | |
|   //
 | |
|   RestorePageNumber = 2;
 | |
|   RestoreAddress = PFAddress;
 | |
|   while (RestorePageNumber > 0) {
 | |
|     if (RestoreAddress <= 0xFFFFFFFF) {
 | |
|       RestorePageTableBelow4G (PageTable, RestoreAddress, CpuIndex, ErrorCode);
 | |
|     } else {
 | |
|       RestorePageTableAbove4G (PageTable, RestoreAddress, CpuIndex, ErrorCode, &IsValidPFAddress);
 | |
|     }
 | |
|     RestoreAddress += EFI_PAGE_SIZE;
 | |
|     RestorePageNumber--;
 | |
|   }
 | |
| 
 | |
|   if (!IsValidPFAddress) {
 | |
|     InstructionAddress = Rip;
 | |
|     if ((ErrorCode & IA32_PF_EC_ID) != 0 && (mBtsSupported)) {
 | |
|       //
 | |
|       // If it is instruction fetch failure, get the correct IP from BTS.
 | |
|       //
 | |
|       InstructionAddress = GetSourceFromDestinationOnBts (CpuIndex, Rip);
 | |
|       if (InstructionAddress == 0) {
 | |
|         //
 | |
|         // It indicates the instruction which caused page fault is not a jump instruction,
 | |
|         // set instruction address same as the page fault address.
 | |
|         //
 | |
|         InstructionAddress = PFAddress;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Indicate it is not software SMI
 | |
|     //
 | |
|     SmiCommand    = 0xFFFFFFFFFFFFFFFFULL;
 | |
|     for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
 | |
|       Status = SmmReadSaveState(&mSmmCpu, sizeof(IoInfo), EFI_SMM_SAVE_STATE_REGISTER_IO, Index, &IoInfo);
 | |
|       if (EFI_ERROR (Status)) {
 | |
|         continue;
 | |
|       }
 | |
|       if (IoInfo.IoPort == mSmiCommandPort) {
 | |
|         //
 | |
|         // A software SMI triggered by SMI command port has been found, get SmiCommand from SMI command port.
 | |
|         //
 | |
|         SoftSmiValue = IoRead8 (mSmiCommandPort);
 | |
|         SmiCommand = (UINT64)SoftSmiValue;
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     SmmProfileEntry = (SMM_PROFILE_ENTRY *)(UINTN)(mSmmProfileBase + 1);
 | |
|     //
 | |
|     // Check if there is already a same entry in profile data.
 | |
|     //
 | |
|     for (Index = 0; Index < (UINTN) mSmmProfileBase->CurDataEntries; Index++) {
 | |
|       if ((SmmProfileEntry[Index].ErrorCode   == (UINT64)ErrorCode) &&
 | |
|           (SmmProfileEntry[Index].Address     == PFAddress) &&
 | |
|           (SmmProfileEntry[Index].CpuNum      == (UINT64)CpuIndex) &&
 | |
|           (SmmProfileEntry[Index].Instruction == InstructionAddress) &&
 | |
|           (SmmProfileEntry[Index].SmiCmd      == SmiCommand)) {
 | |
|         //
 | |
|         // Same record exist, need not save again.
 | |
|         //
 | |
|         break;
 | |
|       }
 | |
|     }
 | |
|     if (Index == mSmmProfileBase->CurDataEntries) {
 | |
|       CurrentEntryNumber = (UINTN) mSmmProfileBase->CurDataEntries;
 | |
|       MaxEntryNumber     = (UINTN) mSmmProfileBase->MaxDataEntries;
 | |
|       if (FeaturePcdGet (PcdCpuSmmProfileRingBuffer)) {
 | |
|         CurrentEntryNumber = CurrentEntryNumber % MaxEntryNumber;
 | |
|       }
 | |
|       if (CurrentEntryNumber < MaxEntryNumber) {
 | |
|         //
 | |
|         // Log the new entry
 | |
|         //
 | |
|         SmmProfileEntry[CurrentEntryNumber].SmiNum      = mSmmProfileBase->NumSmis;
 | |
|         SmmProfileEntry[CurrentEntryNumber].ErrorCode   = (UINT64)ErrorCode;
 | |
|         SmmProfileEntry[CurrentEntryNumber].ApicId      = (UINT64)GetApicId ();
 | |
|         SmmProfileEntry[CurrentEntryNumber].CpuNum      = (UINT64)CpuIndex;
 | |
|         SmmProfileEntry[CurrentEntryNumber].Address     = PFAddress;
 | |
|         SmmProfileEntry[CurrentEntryNumber].Instruction = InstructionAddress;
 | |
|         SmmProfileEntry[CurrentEntryNumber].SmiCmd      = SmiCommand;
 | |
|         //
 | |
|         // Update current entry index and data size in the header.
 | |
|         //
 | |
|         mSmmProfileBase->CurDataEntries++;
 | |
|         mSmmProfileBase->CurDataSize = MultU64x64 (mSmmProfileBase->CurDataEntries, sizeof (SMM_PROFILE_ENTRY));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   //
 | |
|   // Flush TLB
 | |
|   //
 | |
|   CpuFlushTlb ();
 | |
| 
 | |
|   if (mBtsSupported) {
 | |
|     EnableBTS ();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Replace INT1 exception handler to restore page table to absent/execute-disable state
 | |
|   in order to trigger page fault again to save SMM profile data..
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| InitIdtr (
 | |
|   VOID
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS                        Status;
 | |
| 
 | |
|   Status = SmmRegisterExceptionHandler (&mSmmCpuService, EXCEPT_IA32_DEBUG, DebugExceptionHandler);
 | |
|   ASSERT_EFI_ERROR (Status);
 | |
| }
 |