4 Copyright (c) 2012 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
15 #include "PiSmmCpuDxeSmm.h"
16 #include "SmmProfileInternal.h"
18 UINT32 mSmmProfileCr3
;
20 SMM_PROFILE_HEADER
*mSmmProfileBase
;
21 MSR_DS_AREA_STRUCT
*mMsrDsAreaBase
;
23 // The buffer to store SMM profile data.
25 UINTN mSmmProfileSize
;
28 // The buffer to enable branch trace store.
30 UINTN mMsrDsAreaSize
= SMM_PROFILE_DTS_SIZE
;
33 // The flag indicates if execute-disable is supported by processor.
35 BOOLEAN mXdSupported
= FALSE
;
38 // The flag indicates if execute-disable is enabled on processor.
40 BOOLEAN mXdEnabled
= FALSE
;
43 // The flag indicates if BTS is supported by processor.
45 BOOLEAN mBtsSupported
= FALSE
;
48 // The flag indicates if SMM profile starts to record data.
50 BOOLEAN mSmmProfileStart
= FALSE
;
53 // Record the page fault exception count for one instruction execution.
57 UINT64 (*mLastPFEntryValue
)[MAX_PF_ENTRY_COUNT
];
58 UINT64
*(*mLastPFEntryPointer
)[MAX_PF_ENTRY_COUNT
];
60 MSR_DS_AREA_STRUCT
**mMsrDsArea
;
61 BRANCH_TRACE_RECORD
**mMsrBTSRecord
;
62 UINTN mBTSRecordNumber
;
63 PEBS_RECORD
**mMsrPEBSRecord
;
66 // These memory ranges are always present, they does not generate the access type of page fault exception,
67 // but they possibly generate instruction fetch type of page fault exception.
69 MEMORY_PROTECTION_RANGE
*mProtectionMemRange
= NULL
;
70 UINTN mProtectionMemRangeCount
= 0;
73 // Some predefined memory ranges.
75 MEMORY_PROTECTION_RANGE mProtectionMemRangeTemplate
[] = {
77 // SMRAM range (to be fixed in runtime).
78 // It is always present and instruction fetches are allowed.
80 {{0x00000000, 0x00000000},TRUE
,FALSE
},
83 // SMM profile data range( to be fixed in runtime).
84 // It is always present and instruction fetches are not allowed.
86 {{0x00000000, 0x00000000},TRUE
,TRUE
},
89 // Future extended range could be added here.
93 // PCI MMIO ranges (to be added in runtime).
94 // They are always present and instruction fetches are not allowed.
99 // These memory ranges are mapped by 4KB-page instead of 2MB-page.
101 MEMORY_RANGE
*mSplitMemRange
= NULL
;
102 UINTN mSplitMemRangeCount
= 0;
107 UINT32 mSmiCommandPort
;
110 Disable branch trace store.
118 AsmMsrAnd64 (MSR_DEBUG_CTL
, ~((UINT64
)(MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
)));
122 Enable branch trace store.
130 AsmMsrOr64 (MSR_DEBUG_CTL
, (MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
));
134 Get CPU Index from APIC ID.
145 ApicId
= GetApicId ();
147 for (Index
= 0; Index
< PcdGet32 (PcdCpuMaxLogicalProcessorNumber
); Index
++) {
148 if (gSmmCpuPrivate
->ProcessorInfo
[Index
].ProcessorId
== ApicId
) {
157 Get the source of IP after execute-disable exception is triggered.
159 @param CpuIndex The index of CPU.
160 @param DestinationIP The destination address.
164 GetSourceFromDestinationOnBts (
169 BRANCH_TRACE_RECORD
*CurrentBTSRecord
;
175 CurrentBTSRecord
= (BRANCH_TRACE_RECORD
*)mMsrDsArea
[CpuIndex
]->BTSIndex
;
176 for (Index
= 0; Index
< mBTSRecordNumber
; Index
++) {
177 if ((UINTN
)CurrentBTSRecord
< (UINTN
)mMsrBTSRecord
[CpuIndex
]) {
181 CurrentBTSRecord
= (BRANCH_TRACE_RECORD
*)((UINTN
)mMsrDsArea
[CpuIndex
]->BTSAbsoluteMaximum
- 1);
184 if (CurrentBTSRecord
->LastBranchTo
== DestinationIP
) {
186 // Good! find 1st one, then find 2nd one.
190 // The first one is DEBUG exception
195 // Good find proper one.
197 return CurrentBTSRecord
->LastBranchFrom
;
207 SMM profile specific INT 1 (single-step) exception handler.
209 @param InterruptType Defines the type of interrupt or exception that
210 occurred on the processor.This parameter is processor architecture specific.
211 @param SystemContext A pointer to the processor context when
212 the interrupt occurred on the processor.
216 DebugExceptionHandler (
217 IN EFI_EXCEPTION_TYPE InterruptType
,
218 IN EFI_SYSTEM_CONTEXT SystemContext
224 if (!mSmmProfileStart
) {
227 CpuIndex
= GetCpuIndex ();
230 // Clear last PF entries
232 for (PFEntry
= 0; PFEntry
< mPFEntryCount
[CpuIndex
]; PFEntry
++) {
233 *mLastPFEntryPointer
[CpuIndex
][PFEntry
] = mLastPFEntryValue
[CpuIndex
][PFEntry
];
237 // Reset page fault exception count for next page fault.
239 mPFEntryCount
[CpuIndex
] = 0;
247 // Clear TF in EFLAGS
249 ClearTrapFlag (SystemContext
);
253 Check if the memory address will be mapped by 4KB-page.
255 @param Address The address of Memory.
256 @param Nx The flag indicates if the memory is execute-disable.
261 IN EFI_PHYSICAL_ADDRESS Address
,
268 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
270 // Check configuration
272 for (Index
= 0; Index
< mProtectionMemRangeCount
; Index
++) {
273 if ((Address
>= mProtectionMemRange
[Index
].Range
.Base
) && (Address
< mProtectionMemRange
[Index
].Range
.Top
)) {
274 *Nx
= mProtectionMemRange
[Index
].Nx
;
275 return mProtectionMemRange
[Index
].Present
;
282 if ((Address
< mCpuHotPlugData
.SmrrBase
) ||
283 (Address
>= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
)) {
291 Check if the memory address will be mapped by 4KB-page.
293 @param Address The address of Memory.
298 IN EFI_PHYSICAL_ADDRESS Address
303 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
305 // Check configuration
307 for (Index
= 0; Index
< mSplitMemRangeCount
; Index
++) {
308 if ((Address
>= mSplitMemRange
[Index
].Base
) && (Address
< mSplitMemRange
[Index
].Top
)) {
313 if (Address
< mCpuHotPlugData
.SmrrBase
) {
314 if ((mCpuHotPlugData
.SmrrBase
- Address
) < BASE_2MB
) {
317 } else if (Address
> (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
- BASE_2MB
)) {
318 if ((Address
- (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
- BASE_2MB
)) < BASE_2MB
) {
330 Initialize the protected memory ranges and the 4KB-page mapped memory ranges.
334 InitProtectedMemRange (
339 UINTN NumberOfDescriptors
;
340 UINTN NumberOfMmioDescriptors
;
341 UINTN NumberOfProtectRange
;
342 UINTN NumberOfSpliteRange
;
343 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
345 EFI_PHYSICAL_ADDRESS ProtectBaseAddress
;
346 EFI_PHYSICAL_ADDRESS ProtectEndAddress
;
347 EFI_PHYSICAL_ADDRESS Top2MBAlignedAddress
;
348 EFI_PHYSICAL_ADDRESS Base2MBAlignedAddress
;
349 UINT64 High4KBPageSize
;
350 UINT64 Low4KBPageSize
;
352 NumberOfDescriptors
= 0;
353 NumberOfMmioDescriptors
= 0;
354 NumberOfSpliteRange
= 0;
355 MemorySpaceMap
= NULL
;
358 // Get MMIO ranges from GCD and add them into protected memory ranges.
360 gDS
->GetMemorySpaceMap (
361 &NumberOfDescriptors
,
364 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
365 if (MemorySpaceMap
[Index
].GcdMemoryType
== EfiGcdMemoryTypeMemoryMappedIo
) {
366 NumberOfMmioDescriptors
++;
370 if (NumberOfMmioDescriptors
!= 0) {
371 TotalSize
= NumberOfMmioDescriptors
* sizeof (MEMORY_PROTECTION_RANGE
) + sizeof (mProtectionMemRangeTemplate
);
372 mProtectionMemRange
= (MEMORY_PROTECTION_RANGE
*) AllocateZeroPool (TotalSize
);
373 ASSERT (mProtectionMemRange
!= NULL
);
374 mProtectionMemRangeCount
= TotalSize
/ sizeof (MEMORY_PROTECTION_RANGE
);
377 // Copy existing ranges.
379 CopyMem (mProtectionMemRange
, mProtectionMemRangeTemplate
, sizeof (mProtectionMemRangeTemplate
));
382 // Create split ranges which come from protected ranges.
384 TotalSize
= (TotalSize
/ sizeof (MEMORY_PROTECTION_RANGE
)) * sizeof (MEMORY_RANGE
);
385 mSplitMemRange
= (MEMORY_RANGE
*) AllocateZeroPool (TotalSize
);
386 ASSERT (mSplitMemRange
!= NULL
);
389 // Create MMIO ranges which are set to present and execution-disable.
391 NumberOfProtectRange
= sizeof (mProtectionMemRangeTemplate
) / sizeof (MEMORY_PROTECTION_RANGE
);
392 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
393 if (MemorySpaceMap
[Index
].GcdMemoryType
!= EfiGcdMemoryTypeMemoryMappedIo
) {
396 mProtectionMemRange
[NumberOfProtectRange
].Range
.Base
= MemorySpaceMap
[Index
].BaseAddress
;
397 mProtectionMemRange
[NumberOfProtectRange
].Range
.Top
= MemorySpaceMap
[Index
].BaseAddress
+ MemorySpaceMap
[Index
].Length
;
398 mProtectionMemRange
[NumberOfProtectRange
].Present
= TRUE
;
399 mProtectionMemRange
[NumberOfProtectRange
].Nx
= TRUE
;
400 NumberOfProtectRange
++;
405 // According to protected ranges, create the ranges which will be mapped by 2KB page.
407 NumberOfSpliteRange
= 0;
408 NumberOfProtectRange
= mProtectionMemRangeCount
;
409 for (Index
= 0; Index
< NumberOfProtectRange
; Index
++) {
411 // If MMIO base address is not 2MB alignment, make 2MB alignment for create 4KB page in page table.
413 ProtectBaseAddress
= mProtectionMemRange
[Index
].Range
.Base
;
414 ProtectEndAddress
= mProtectionMemRange
[Index
].Range
.Top
;
415 if (((ProtectBaseAddress
& (SIZE_2MB
- 1)) != 0) || ((ProtectEndAddress
& (SIZE_2MB
- 1)) != 0)) {
417 // Check if it is possible to create 4KB-page for not 2MB-aligned range and to create 2MB-page for 2MB-aligned range.
418 // A mix of 4KB and 2MB page could save SMRAM space.
420 Top2MBAlignedAddress
= ProtectEndAddress
& ~(SIZE_2MB
- 1);
421 Base2MBAlignedAddress
= (ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
422 if ((Top2MBAlignedAddress
> Base2MBAlignedAddress
) &&
423 ((Top2MBAlignedAddress
- Base2MBAlignedAddress
) >= SIZE_2MB
)) {
425 // There is an range which could be mapped by 2MB-page.
427 High4KBPageSize
= ((ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1)) - (ProtectEndAddress
& ~(SIZE_2MB
- 1));
428 Low4KBPageSize
= ((ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1)) - (ProtectBaseAddress
& ~(SIZE_2MB
- 1));
429 if (High4KBPageSize
!= 0) {
431 // Add not 2MB-aligned range to be mapped by 4KB-page.
433 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectEndAddress
& ~(SIZE_2MB
- 1);
434 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
435 NumberOfSpliteRange
++;
437 if (Low4KBPageSize
!= 0) {
439 // Add not 2MB-aligned range to be mapped by 4KB-page.
441 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectBaseAddress
& ~(SIZE_2MB
- 1);
442 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
443 NumberOfSpliteRange
++;
447 // The range could only be mapped by 4KB-page.
449 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectBaseAddress
& ~(SIZE_2MB
- 1);
450 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
451 NumberOfSpliteRange
++;
456 mSplitMemRangeCount
= NumberOfSpliteRange
;
458 DEBUG ((EFI_D_INFO
, "SMM Profile Memory Ranges:\n"));
459 for (Index
= 0; Index
< mProtectionMemRangeCount
; Index
++) {
460 DEBUG ((EFI_D_INFO
, "mProtectionMemRange[%d].Base = %lx\n", Index
, mProtectionMemRange
[Index
].Range
.Base
));
461 DEBUG ((EFI_D_INFO
, "mProtectionMemRange[%d].Top = %lx\n", Index
, mProtectionMemRange
[Index
].Range
.Top
));
463 for (Index
= 0; Index
< mSplitMemRangeCount
; Index
++) {
464 DEBUG ((EFI_D_INFO
, "mSplitMemRange[%d].Base = %lx\n", Index
, mSplitMemRange
[Index
].Base
));
465 DEBUG ((EFI_D_INFO
, "mSplitMemRange[%d].Top = %lx\n", Index
, mSplitMemRange
[Index
].Top
));
470 Update page table according to protected memory ranges and the 4KB-page mapped memory ranges.
487 UINTN NumberOfPdpEntries
;
488 UINTN NumberOfPml4Entries
;
489 UINTN SizeOfMemorySpace
;
492 if (sizeof (UINTN
) == sizeof (UINT64
)) {
493 Pml4
= (UINT64
*)(UINTN
)mSmmProfileCr3
;
494 SizeOfMemorySpace
= HighBitSet64 (gPhyMask
) + 1;
496 // Calculate the table entries of PML4E and PDPTE.
498 if (SizeOfMemorySpace
<= 39 ) {
499 NumberOfPml4Entries
= 1;
500 NumberOfPdpEntries
= (UINT32
)LShiftU64 (1, (SizeOfMemorySpace
- 30));
502 NumberOfPml4Entries
= (UINT32
)LShiftU64 (1, (SizeOfMemorySpace
- 39));
503 NumberOfPdpEntries
= 512;
506 NumberOfPml4Entries
= 1;
507 NumberOfPdpEntries
= 4;
511 // Go through page table and change 2MB-page into 4KB-page.
513 for (Level1
= 0; Level1
< NumberOfPml4Entries
; Level1
++) {
514 if (sizeof (UINTN
) == sizeof (UINT64
)) {
515 if ((Pml4
[Level1
] & IA32_PG_P
) == 0) {
517 // If Pml4 entry does not exist, skip it
521 Pde
= (UINT64
*)(UINTN
)(Pml4
[Level1
] & PHYSICAL_ADDRESS_MASK
);
523 Pde
= (UINT64
*)(UINTN
)mSmmProfileCr3
;
525 for (Level2
= 0; Level2
< NumberOfPdpEntries
; Level2
++, Pde
++) {
526 if ((*Pde
& IA32_PG_P
) == 0) {
528 // If PDE entry does not exist, skip it
532 Pte
= (UINT64
*)(UINTN
)(*Pde
& PHYSICAL_ADDRESS_MASK
);
536 for (Level3
= 0; Level3
< SIZE_4KB
/ sizeof (*Pte
); Level3
++, Pte
++) {
537 if ((*Pte
& IA32_PG_P
) == 0) {
539 // If PTE entry does not exist, skip it
543 Address
= (((Level2
<< 9) + Level3
) << 21);
546 // If it is 2M page, check IsAddressSplit()
548 if (((*Pte
& IA32_PG_PS
) != 0) && IsAddressSplit (Address
)) {
550 // Based on current page table, create 4KB page table for split area.
552 ASSERT (Address
== (*Pte
& PHYSICAL_ADDRESS_MASK
));
554 Pt
= AllocatePageTableMemory (1);
558 for (Level4
= 0; Level4
< SIZE_4KB
/ sizeof(*Pt
); Level4
++) {
559 Pt
[Level4
] = Address
+ ((Level4
<< 12) | PAGE_ATTRIBUTE_BITS
);
561 *Pte
= (UINTN
)Pt
| PAGE_ATTRIBUTE_BITS
;
562 } // end if IsAddressSplit
568 // Go through page table and set several page table entries to absent or execute-disable.
570 DEBUG ((EFI_D_INFO
, "Patch page table start ...\n"));
571 for (Level1
= 0; Level1
< NumberOfPml4Entries
; Level1
++) {
572 if (sizeof (UINTN
) == sizeof (UINT64
)) {
573 if ((Pml4
[Level1
] & IA32_PG_P
) == 0) {
575 // If Pml4 entry does not exist, skip it
579 Pde
= (UINT64
*)(UINTN
)(Pml4
[Level1
] & PHYSICAL_ADDRESS_MASK
);
581 Pde
= (UINT64
*)(UINTN
)mSmmProfileCr3
;
583 for (Level2
= 0; Level2
< NumberOfPdpEntries
; Level2
++, Pde
++) {
584 if ((*Pde
& IA32_PG_P
) == 0) {
586 // If PDE entry does not exist, skip it
590 Pte
= (UINT64
*)(UINTN
)(*Pde
& PHYSICAL_ADDRESS_MASK
);
594 for (Level3
= 0; Level3
< SIZE_4KB
/ sizeof (*Pte
); Level3
++, Pte
++) {
595 if ((*Pte
& IA32_PG_P
) == 0) {
597 // If PTE entry does not exist, skip it
601 Address
= (((Level2
<< 9) + Level3
) << 21);
603 if ((*Pte
& IA32_PG_PS
) != 0) {
606 if (!IsAddressValid (Address
, &Nx
)) {
608 // Patch to remove Present flag and RW flag
610 *Pte
= *Pte
& (INTN
)(INT32
)(~PAGE_ATTRIBUTE_BITS
);
612 if (Nx
&& mXdSupported
) {
613 *Pte
= *Pte
| IA32_PG_NX
;
617 Pt
= (UINT64
*)(UINTN
)(*Pte
& PHYSICAL_ADDRESS_MASK
);
621 for (Level4
= 0; Level4
< SIZE_4KB
/ sizeof(*Pt
); Level4
++, Pt
++) {
622 if (!IsAddressValid (Address
, &Nx
)) {
623 *Pt
= *Pt
& (INTN
)(INT32
)(~PAGE_ATTRIBUTE_BITS
);
625 if (Nx
&& mXdSupported
) {
626 *Pt
= *Pt
| IA32_PG_NX
;
639 DEBUG ((EFI_D_INFO
, "Patch page table done!\n"));
641 // Set execute-disable flag
649 To find FADT in ACPI tables.
651 @param AcpiTableGuid The GUID used to find ACPI table in UEFI ConfigurationTable.
653 @return FADT table pointer.
655 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*
656 FindAcpiFadtTableByAcpiGuid (
657 IN EFI_GUID
*AcpiTableGuid
660 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
661 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
662 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
669 // found ACPI table RSD_PTR from system table
671 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
672 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
674 // A match was found.
676 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
685 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
686 if (Rsdt
== NULL
|| Rsdt
->Signature
!= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
690 for (Index
= sizeof (EFI_ACPI_DESCRIPTION_HEADER
); Index
< Rsdt
->Length
; Index
= Index
+ sizeof (UINT32
)) {
692 Data32
= *(UINT32
*) ((UINT8
*) Rsdt
+ Index
);
693 Fadt
= (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*) (UINT32
*) (UINTN
) Data32
;
694 if (Fadt
->Header
.Signature
== EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
) {
699 if (Fadt
== NULL
|| Fadt
->Header
.Signature
!= EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
) {
707 To find FADT in ACPI tables.
709 @return FADT table pointer.
711 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*
716 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
718 Fadt
= FindAcpiFadtTableByAcpiGuid (&gEfiAcpi20TableGuid
);
723 return FindAcpiFadtTableByAcpiGuid (&gEfiAcpi10TableGuid
);
727 To get system port address of the SMI Command Port in FADT table.
735 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
737 Fadt
= FindAcpiFadtTable ();
738 ASSERT (Fadt
!= NULL
);
740 mSmiCommandPort
= Fadt
->SmiCmd
;
741 DEBUG ((EFI_D_INFO
, "mSmiCommandPort = %x\n", mSmiCommandPort
));
745 Updates page table to make some memory ranges (like system memory) absent
746 and make some memory ranges (like MMIO) present and execute disable. It also
747 update 2MB-page to 4KB-page for some memory ranges.
756 // The flag indicates SMM profile starts to work.
758 mSmmProfileStart
= TRUE
;
762 Initialize SMM profile in SmmReadyToLock protocol callback function.
764 @param Protocol Points to the protocol's unique identifier.
765 @param Interface Points to the interface instance.
766 @param Handle The handle on which the interface was installed.
768 @retval EFI_SUCCESS SmmReadyToLock protocol callback runs successfully.
772 InitSmmProfileCallBack (
773 IN CONST EFI_GUID
*Protocol
,
779 // Save to variable so that SMM profile data can be found.
784 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
785 sizeof(mSmmProfileBase
),
790 // Get Software SMI from FADT
792 GetSmiCommandPort ();
795 // Initialize protected memory range for patching page table later.
797 InitProtectedMemRange ();
803 Initialize SMM profile data structures.
807 InitSmmProfileInternal (
812 EFI_PHYSICAL_ADDRESS Base
;
815 UINTN MsrDsAreaSizePerCpu
;
818 mPFEntryCount
= (UINTN
*)AllocateZeroPool (sizeof (UINTN
) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber
));
819 ASSERT (mPFEntryCount
!= NULL
);
820 mLastPFEntryValue
= (UINT64 (*)[MAX_PF_ENTRY_COUNT
])AllocateZeroPool (
821 sizeof (mLastPFEntryValue
[0]) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber
));
822 ASSERT (mLastPFEntryValue
!= NULL
);
823 mLastPFEntryPointer
= (UINT64
*(*)[MAX_PF_ENTRY_COUNT
])AllocateZeroPool (
824 sizeof (mLastPFEntryPointer
[0]) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber
));
825 ASSERT (mLastPFEntryPointer
!= NULL
);
828 // Allocate memory for SmmProfile below 4GB.
831 mSmmProfileSize
= PcdGet32 (PcdCpuSmmProfileSize
);
832 ASSERT ((mSmmProfileSize
& 0xFFF) == 0);
835 TotalSize
= mSmmProfileSize
+ mMsrDsAreaSize
;
837 TotalSize
= mSmmProfileSize
;
841 Status
= gBS
->AllocatePages (
843 EfiReservedMemoryType
,
844 EFI_SIZE_TO_PAGES (TotalSize
),
847 ASSERT_EFI_ERROR (Status
);
848 ZeroMem ((VOID
*)(UINTN
)Base
, TotalSize
);
849 mSmmProfileBase
= (SMM_PROFILE_HEADER
*)(UINTN
)Base
;
852 // Initialize SMM profile data header.
854 mSmmProfileBase
->HeaderSize
= sizeof (SMM_PROFILE_HEADER
);
855 mSmmProfileBase
->MaxDataEntries
= (UINT64
)((mSmmProfileSize
- sizeof(SMM_PROFILE_HEADER
)) / sizeof (SMM_PROFILE_ENTRY
));
856 mSmmProfileBase
->MaxDataSize
= MultU64x64 (mSmmProfileBase
->MaxDataEntries
, sizeof(SMM_PROFILE_ENTRY
));
857 mSmmProfileBase
->CurDataEntries
= 0;
858 mSmmProfileBase
->CurDataSize
= 0;
859 mSmmProfileBase
->TsegStart
= mCpuHotPlugData
.SmrrBase
;
860 mSmmProfileBase
->TsegSize
= mCpuHotPlugData
.SmrrSize
;
861 mSmmProfileBase
->NumSmis
= 0;
862 mSmmProfileBase
->NumCpus
= gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
;
865 mMsrDsArea
= (MSR_DS_AREA_STRUCT
**)AllocateZeroPool (sizeof (MSR_DS_AREA_STRUCT
*) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber
));
866 ASSERT (mMsrDsArea
!= NULL
);
867 mMsrBTSRecord
= (BRANCH_TRACE_RECORD
**)AllocateZeroPool (sizeof (BRANCH_TRACE_RECORD
*) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber
));
868 ASSERT (mMsrBTSRecord
!= NULL
);
869 mMsrPEBSRecord
= (PEBS_RECORD
**)AllocateZeroPool (sizeof (PEBS_RECORD
*) * PcdGet32 (PcdCpuMaxLogicalProcessorNumber
));
870 ASSERT (mMsrPEBSRecord
!= NULL
);
872 mMsrDsAreaBase
= (MSR_DS_AREA_STRUCT
*)((UINTN
)Base
+ mSmmProfileSize
);
873 MsrDsAreaSizePerCpu
= mMsrDsAreaSize
/ PcdGet32 (PcdCpuMaxLogicalProcessorNumber
);
874 mBTSRecordNumber
= (MsrDsAreaSizePerCpu
- sizeof(PEBS_RECORD
) * PEBS_RECORD_NUMBER
- sizeof(MSR_DS_AREA_STRUCT
)) / sizeof(BRANCH_TRACE_RECORD
);
875 for (Index
= 0; Index
< PcdGet32 (PcdCpuMaxLogicalProcessorNumber
); Index
++) {
876 mMsrDsArea
[Index
] = (MSR_DS_AREA_STRUCT
*)((UINTN
)mMsrDsAreaBase
+ MsrDsAreaSizePerCpu
* Index
);
877 mMsrBTSRecord
[Index
] = (BRANCH_TRACE_RECORD
*)((UINTN
)mMsrDsArea
[Index
] + sizeof(MSR_DS_AREA_STRUCT
));
878 mMsrPEBSRecord
[Index
] = (PEBS_RECORD
*)((UINTN
)mMsrDsArea
[Index
] + MsrDsAreaSizePerCpu
- sizeof(PEBS_RECORD
) * PEBS_RECORD_NUMBER
);
880 mMsrDsArea
[Index
]->BTSBufferBase
= (UINTN
)mMsrBTSRecord
[Index
];
881 mMsrDsArea
[Index
]->BTSIndex
= mMsrDsArea
[Index
]->BTSBufferBase
;
882 mMsrDsArea
[Index
]->BTSAbsoluteMaximum
= mMsrDsArea
[Index
]->BTSBufferBase
+ mBTSRecordNumber
* sizeof(BRANCH_TRACE_RECORD
) + 1;
883 mMsrDsArea
[Index
]->BTSInterruptThreshold
= mMsrDsArea
[Index
]->BTSAbsoluteMaximum
+ 1;
885 mMsrDsArea
[Index
]->PEBSBufferBase
= (UINTN
)mMsrPEBSRecord
[Index
];
886 mMsrDsArea
[Index
]->PEBSIndex
= mMsrDsArea
[Index
]->PEBSBufferBase
;
887 mMsrDsArea
[Index
]->PEBSAbsoluteMaximum
= mMsrDsArea
[Index
]->PEBSBufferBase
+ PEBS_RECORD_NUMBER
* sizeof(PEBS_RECORD
) + 1;
888 mMsrDsArea
[Index
]->PEBSInterruptThreshold
= mMsrDsArea
[Index
]->PEBSAbsoluteMaximum
+ 1;
892 mProtectionMemRange
= mProtectionMemRangeTemplate
;
893 mProtectionMemRangeCount
= sizeof (mProtectionMemRangeTemplate
) / sizeof (MEMORY_PROTECTION_RANGE
);
896 // Update TSeg entry.
898 mProtectionMemRange
[0].Range
.Base
= mCpuHotPlugData
.SmrrBase
;
899 mProtectionMemRange
[0].Range
.Top
= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
;
902 // Update SMM profile entry.
904 mProtectionMemRange
[1].Range
.Base
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)mSmmProfileBase
;
905 mProtectionMemRange
[1].Range
.Top
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)mSmmProfileBase
+ TotalSize
;
908 // Allocate memory reserved for creating 4KB pages.
910 InitPagesForPFHandler ();
913 // Start SMM profile when SmmReadyToLock protocol is installed.
915 Status
= gSmst
->SmmRegisterProtocolNotify (
916 &gEfiSmmReadyToLockProtocolGuid
,
917 InitSmmProfileCallBack
,
920 ASSERT_EFI_ERROR (Status
);
926 Check if XD feature is supported by a processor.
928 @param[in,out] Buffer The pointer to private data buffer.
933 CheckFeatureSupported (
939 MSR_IA32_MISC_ENABLE_REGISTER MiscEnableMsr
;
942 AsmCpuid (CPUID_EXTENDED_FUNCTION
, &RegEax
, NULL
, NULL
, NULL
);
943 if (RegEax
<= CPUID_EXTENDED_FUNCTION
) {
945 // Extended CPUID functions are not supported on this processor.
947 mXdSupported
= FALSE
;
950 AsmCpuid (CPUID_EXTENDED_CPU_SIG
, NULL
, NULL
, NULL
, &RegEdx
);
951 if ((RegEdx
& CPUID1_EDX_XD_SUPPORT
) == 0) {
953 // Execute Disable Bit feature is not supported on this processor.
955 mXdSupported
= FALSE
;
960 AsmCpuid (CPUID_VERSION_INFO
, NULL
, NULL
, NULL
, &RegEdx
);
961 if ((RegEdx
& CPUID1_EDX_BTS_AVAILABLE
) != 0) {
964 // When CPUID.1:EDX[21] is set, the following BTS facilities are available:
965 // 1. The BTS_UNAVAILABLE flag in the IA32_MISC_ENABLE MSR indicates the
966 // availability of the BTS facilities, including the ability to set the BTS and
967 // BTINT bits in the MSR_DEBUGCTLA MSR.
968 // 2. The IA32_DS_AREA MSR can be programmed to point to the DS save area.
970 MiscEnableMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_MISC_ENABLE
);
971 if (MiscEnableMsr
.Bits
.BTS
== 1) {
973 // BTS facilities is not supported if MSR_IA32_MISC_ENABLE.BTS bit is set.
975 mBtsSupported
= FALSE
;
982 Check if XD and BTS features are supported by all processors.
986 CheckProcessorFeature (
991 EFI_MP_SERVICES_PROTOCOL
*MpServices
;
993 Status
= gBS
->LocateProtocol (&gEfiMpServiceProtocolGuid
, NULL
, (VOID
**)&MpServices
);
994 ASSERT_EFI_ERROR (Status
);
997 // First detect if XD and BTS are supported
1000 mBtsSupported
= TRUE
;
1003 // Check if XD and BTS are supported on all processors.
1005 CheckFeatureSupported (NULL
);
1008 //Check on other processors if BSP supports this
1010 if (mXdSupported
|| mBtsSupported
) {
1011 MpServices
->StartupAllAPs (
1013 CheckFeatureSupported
,
1032 UINT64 MsrRegisters
;
1034 MsrRegisters
= AsmReadMsr64 (MSR_EFER
);
1035 if ((MsrRegisters
& MSR_EFER_XD
) != 0) {
1038 MsrRegisters
|= MSR_EFER_XD
;
1039 AsmWriteMsr64 (MSR_EFER
, MsrRegisters
);
1047 ActivateSingleStepDB (
1053 Dr6
= AsmReadDr6 ();
1054 if ((Dr6
& DR6_SINGLE_STEP
) != 0) {
1057 Dr6
|= DR6_SINGLE_STEP
;
1072 DebugCtl
= AsmReadMsr64 (MSR_DEBUG_CTL
);
1073 if ((DebugCtl
& MSR_DEBUG_CTL_LBR
) != 0) {
1076 DebugCtl
|= MSR_DEBUG_CTL_LBR
;
1077 AsmWriteMsr64 (MSR_DEBUG_CTL
, DebugCtl
);
1081 Enable branch trace store.
1083 @param CpuIndex The index of the processor.
1093 DebugCtl
= AsmReadMsr64 (MSR_DEBUG_CTL
);
1094 if ((DebugCtl
& MSR_DEBUG_CTL_BTS
) != 0) {
1098 AsmWriteMsr64 (MSR_DS_AREA
, (UINT64
)(UINTN
)mMsrDsArea
[CpuIndex
]);
1099 DebugCtl
|= (UINT64
)(MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
);
1100 DebugCtl
&= ~((UINT64
)MSR_DEBUG_CTL_BTINT
);
1101 AsmWriteMsr64 (MSR_DEBUG_CTL
, DebugCtl
);
1105 Increase SMI number in each SMI entry.
1109 SmmProfileRecordSmiNum (
1113 if (mSmmProfileStart
) {
1114 mSmmProfileBase
->NumSmis
++;
1119 Initialize processor environment for SMM profile.
1121 @param CpuIndex The index of the processor.
1125 ActivateSmmProfile (
1130 // Enable Single Step DB#
1132 ActivateSingleStepDB ();
1134 if (mBtsSupported
) {
1136 // We can not get useful information from LER, so we have to use BTS.
1143 ActivateBTS (CpuIndex
);
1148 Initialize SMM profile in SMM CPU entry point.
1150 @param[in] Cr3 The base address of the page tables to use in SMM.
1161 mSmmProfileCr3
= Cr3
;
1164 // Skip SMM profile initialization if feature is disabled
1166 if (!FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
1171 // Initialize SmmProfile here
1173 InitSmmProfileInternal ();
1176 // Initialize profile IDT.
1182 Update page table to map the memory correctly in order to make the instruction
1183 which caused page fault execute successfully. And it also save the original page
1184 table to be restored in single-step exception.
1186 @param PageTable PageTable Address.
1187 @param PFAddress The memory address which caused page fault exception.
1188 @param CpuIndex The index of the processor.
1189 @param ErrorCode The Error code of exception.
1193 RestorePageTableBelow4G (
1206 if (sizeof(UINT64
) == sizeof(UINTN
)) {
1207 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 39, 47);
1208 ASSERT (PageTable
[PTIndex
] != 0);
1209 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1215 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 30, 38);
1216 ASSERT (PageTable
[PTIndex
] != 0);
1217 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1222 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 21, 29);
1223 if ((PageTable
[PTIndex
] & IA32_PG_PS
) != 0) {
1229 // Record old entries with non-present status
1230 // Old entries include the memory which instruction is at and the memory which instruction access.
1233 ASSERT (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
);
1234 if (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
) {
1235 PFIndex
= mPFEntryCount
[CpuIndex
];
1236 mLastPFEntryValue
[CpuIndex
][PFIndex
] = PageTable
[PTIndex
];
1237 mLastPFEntryPointer
[CpuIndex
][PFIndex
] = &PageTable
[PTIndex
];
1238 mPFEntryCount
[CpuIndex
]++;
1244 PageTable
[PTIndex
] = (PFAddress
& ~((1ull << 21) - 1));
1245 PageTable
[PTIndex
] |= (UINT64
)IA32_PG_PS
;
1246 PageTable
[PTIndex
] |= (UINT64
)PAGE_ATTRIBUTE_BITS
;
1247 if ((ErrorCode
& IA32_PF_EC_ID
) != 0) {
1248 PageTable
[PTIndex
] &= ~IA32_PG_NX
;
1254 ASSERT (PageTable
[PTIndex
] != 0);
1255 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1260 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 12, 20);
1263 // Record old entries with non-present status
1264 // Old entries include the memory which instruction is at and the memory which instruction access.
1267 ASSERT (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
);
1268 if (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
) {
1269 PFIndex
= mPFEntryCount
[CpuIndex
];
1270 mLastPFEntryValue
[CpuIndex
][PFIndex
] = PageTable
[PTIndex
];
1271 mLastPFEntryPointer
[CpuIndex
][PFIndex
] = &PageTable
[PTIndex
];
1272 mPFEntryCount
[CpuIndex
]++;
1278 PageTable
[PTIndex
] = (PFAddress
& ~((1ull << 12) - 1));
1279 PageTable
[PTIndex
] |= (UINT64
)PAGE_ATTRIBUTE_BITS
;
1280 if ((ErrorCode
& IA32_PF_EC_ID
) != 0) {
1281 PageTable
[PTIndex
] &= ~IA32_PG_NX
;
1287 The Page fault handler to save SMM profile data.
1289 @param Rip The RIP when exception happens.
1290 @param ErrorCode The Error code of exception.
1294 SmmProfilePFHandler (
1303 UINT64 InstructionAddress
;
1304 UINTN MaxEntryNumber
;
1305 UINTN CurrentEntryNumber
;
1306 BOOLEAN IsValidPFAddress
;
1307 SMM_PROFILE_ENTRY
*SmmProfileEntry
;
1311 EFI_SMM_SAVE_STATE_IO_INFO IoInfo
;
1313 if (!mSmmProfileStart
) {
1315 // If SMM profile does not start, call original page fault handler.
1317 SmiDefaultPFHandler ();
1321 if (mBtsSupported
) {
1325 IsValidPFAddress
= FALSE
;
1326 PageTable
= (UINT64
*)AsmReadCr3 ();
1327 PFAddress
= AsmReadCr2 ();
1328 CpuIndex
= GetCpuIndex ();
1330 if (PFAddress
<= 0xFFFFFFFF) {
1331 RestorePageTableBelow4G (PageTable
, PFAddress
, CpuIndex
, ErrorCode
);
1333 RestorePageTableAbove4G (PageTable
, PFAddress
, CpuIndex
, ErrorCode
, &IsValidPFAddress
);
1336 if (!IsValidPFAddress
) {
1337 InstructionAddress
= Rip
;
1338 if ((ErrorCode
& IA32_PF_EC_ID
) != 0 && (mBtsSupported
)) {
1340 // If it is instruction fetch failure, get the correct IP from BTS.
1342 InstructionAddress
= GetSourceFromDestinationOnBts (CpuIndex
, Rip
);
1343 if (InstructionAddress
== 0) {
1345 // It indicates the instruction which caused page fault is not a jump instruction,
1346 // set instruction address same as the page fault address.
1348 InstructionAddress
= PFAddress
;
1353 // Indicate it is not software SMI
1355 SmiCommand
= 0xFFFFFFFFFFFFFFFFULL
;
1356 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
1357 Status
= SmmReadSaveState(&mSmmCpu
, sizeof(IoInfo
), EFI_SMM_SAVE_STATE_REGISTER_IO
, Index
, &IoInfo
);
1358 if (EFI_ERROR (Status
)) {
1361 if (IoInfo
.IoPort
== mSmiCommandPort
) {
1363 // A software SMI triggered by SMI command port has been found, get SmiCommand from SMI command port.
1365 SoftSmiValue
= IoRead8 (mSmiCommandPort
);
1366 SmiCommand
= (UINT64
)SoftSmiValue
;
1371 SmmProfileEntry
= (SMM_PROFILE_ENTRY
*)(UINTN
)(mSmmProfileBase
+ 1);
1373 // Check if there is already a same entry in profile data.
1375 for (Index
= 0; Index
< (UINTN
) mSmmProfileBase
->CurDataEntries
; Index
++) {
1376 if ((SmmProfileEntry
[Index
].ErrorCode
== (UINT64
)ErrorCode
) &&
1377 (SmmProfileEntry
[Index
].Address
== PFAddress
) &&
1378 (SmmProfileEntry
[Index
].CpuNum
== (UINT64
)CpuIndex
) &&
1379 (SmmProfileEntry
[Index
].Instruction
== InstructionAddress
) &&
1380 (SmmProfileEntry
[Index
].SmiCmd
== SmiCommand
)) {
1382 // Same record exist, need not save again.
1387 if (Index
== mSmmProfileBase
->CurDataEntries
) {
1388 CurrentEntryNumber
= (UINTN
) mSmmProfileBase
->CurDataEntries
;
1389 MaxEntryNumber
= (UINTN
) mSmmProfileBase
->MaxDataEntries
;
1390 if (FeaturePcdGet (PcdCpuSmmProfileRingBuffer
)) {
1391 CurrentEntryNumber
= CurrentEntryNumber
% MaxEntryNumber
;
1393 if (CurrentEntryNumber
< MaxEntryNumber
) {
1395 // Log the new entry
1397 SmmProfileEntry
[CurrentEntryNumber
].SmiNum
= mSmmProfileBase
->NumSmis
;
1398 SmmProfileEntry
[CurrentEntryNumber
].ErrorCode
= (UINT64
)ErrorCode
;
1399 SmmProfileEntry
[CurrentEntryNumber
].ApicId
= (UINT64
)GetApicId ();
1400 SmmProfileEntry
[CurrentEntryNumber
].CpuNum
= (UINT64
)CpuIndex
;
1401 SmmProfileEntry
[CurrentEntryNumber
].Address
= PFAddress
;
1402 SmmProfileEntry
[CurrentEntryNumber
].Instruction
= InstructionAddress
;
1403 SmmProfileEntry
[CurrentEntryNumber
].SmiCmd
= SmiCommand
;
1405 // Update current entry index and data size in the header.
1407 mSmmProfileBase
->CurDataEntries
++;
1408 mSmmProfileBase
->CurDataSize
= MultU64x64 (mSmmProfileBase
->CurDataEntries
, sizeof (SMM_PROFILE_ENTRY
));
1417 if (mBtsSupported
) {
1423 Replace INT1 exception handler to restore page table to absent/execute-disable state
1424 in order to trigger page fault again to save SMM profile data..
1432 SmmRegisterExceptionHandler (&mSmmCpuService
, EXCEPT_IA32_DEBUG
, DebugExceptionHandler
);