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 enabled on processor.
35 BOOLEAN mXdEnabled
= FALSE
;
38 // The flag indicates if BTS is supported by processor.
40 BOOLEAN mBtsSupported
= TRUE
;
43 // The flag indicates if SMM profile starts to record data.
45 BOOLEAN mSmmProfileStart
= FALSE
;
48 // Record the page fault exception count for one instruction execution.
52 UINT64 (*mLastPFEntryValue
)[MAX_PF_ENTRY_COUNT
];
53 UINT64
*(*mLastPFEntryPointer
)[MAX_PF_ENTRY_COUNT
];
55 MSR_DS_AREA_STRUCT
**mMsrDsArea
;
56 BRANCH_TRACE_RECORD
**mMsrBTSRecord
;
57 UINTN mBTSRecordNumber
;
58 PEBS_RECORD
**mMsrPEBSRecord
;
61 // These memory ranges are always present, they does not generate the access type of page fault exception,
62 // but they possibly generate instruction fetch type of page fault exception.
64 MEMORY_PROTECTION_RANGE
*mProtectionMemRange
= NULL
;
65 UINTN mProtectionMemRangeCount
= 0;
68 // Some predefined memory ranges.
70 MEMORY_PROTECTION_RANGE mProtectionMemRangeTemplate
[] = {
72 // SMRAM range (to be fixed in runtime).
73 // It is always present and instruction fetches are allowed.
75 {{0x00000000, 0x00000000},TRUE
,FALSE
},
78 // SMM profile data range( to be fixed in runtime).
79 // It is always present and instruction fetches are not allowed.
81 {{0x00000000, 0x00000000},TRUE
,TRUE
},
84 // Future extended range could be added here.
88 // PCI MMIO ranges (to be added in runtime).
89 // They are always present and instruction fetches are not allowed.
94 // These memory ranges are mapped by 4KB-page instead of 2MB-page.
96 MEMORY_RANGE
*mSplitMemRange
= NULL
;
97 UINTN mSplitMemRangeCount
= 0;
102 UINT32 mSmiCommandPort
;
105 Disable branch trace store.
113 AsmMsrAnd64 (MSR_DEBUG_CTL
, ~((UINT64
)(MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
)));
117 Enable branch trace store.
125 AsmMsrOr64 (MSR_DEBUG_CTL
, (MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
));
129 Get CPU Index from APIC ID.
140 ApicId
= GetApicId ();
142 for (Index
= 0; Index
< mMaxNumberOfCpus
; Index
++) {
143 if (gSmmCpuPrivate
->ProcessorInfo
[Index
].ProcessorId
== ApicId
) {
152 Get the source of IP after execute-disable exception is triggered.
154 @param CpuIndex The index of CPU.
155 @param DestinationIP The destination address.
159 GetSourceFromDestinationOnBts (
164 BRANCH_TRACE_RECORD
*CurrentBTSRecord
;
170 CurrentBTSRecord
= (BRANCH_TRACE_RECORD
*)mMsrDsArea
[CpuIndex
]->BTSIndex
;
171 for (Index
= 0; Index
< mBTSRecordNumber
; Index
++) {
172 if ((UINTN
)CurrentBTSRecord
< (UINTN
)mMsrBTSRecord
[CpuIndex
]) {
176 CurrentBTSRecord
= (BRANCH_TRACE_RECORD
*)((UINTN
)mMsrDsArea
[CpuIndex
]->BTSAbsoluteMaximum
- 1);
179 if (CurrentBTSRecord
->LastBranchTo
== DestinationIP
) {
181 // Good! find 1st one, then find 2nd one.
185 // The first one is DEBUG exception
190 // Good find proper one.
192 return CurrentBTSRecord
->LastBranchFrom
;
202 SMM profile specific INT 1 (single-step) exception handler.
204 @param InterruptType Defines the type of interrupt or exception that
205 occurred on the processor.This parameter is processor architecture specific.
206 @param SystemContext A pointer to the processor context when
207 the interrupt occurred on the processor.
211 DebugExceptionHandler (
212 IN EFI_EXCEPTION_TYPE InterruptType
,
213 IN EFI_SYSTEM_CONTEXT SystemContext
219 if (!mSmmProfileStart
) {
222 CpuIndex
= GetCpuIndex ();
225 // Clear last PF entries
227 for (PFEntry
= 0; PFEntry
< mPFEntryCount
[CpuIndex
]; PFEntry
++) {
228 *mLastPFEntryPointer
[CpuIndex
][PFEntry
] = mLastPFEntryValue
[CpuIndex
][PFEntry
];
232 // Reset page fault exception count for next page fault.
234 mPFEntryCount
[CpuIndex
] = 0;
242 // Clear TF in EFLAGS
244 ClearTrapFlag (SystemContext
);
248 Check if the memory address will be mapped by 4KB-page.
250 @param Address The address of Memory.
251 @param Nx The flag indicates if the memory is execute-disable.
256 IN EFI_PHYSICAL_ADDRESS Address
,
263 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
265 // Check configuration
267 for (Index
= 0; Index
< mProtectionMemRangeCount
; Index
++) {
268 if ((Address
>= mProtectionMemRange
[Index
].Range
.Base
) && (Address
< mProtectionMemRange
[Index
].Range
.Top
)) {
269 *Nx
= mProtectionMemRange
[Index
].Nx
;
270 return mProtectionMemRange
[Index
].Present
;
277 if ((Address
< mCpuHotPlugData
.SmrrBase
) ||
278 (Address
>= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
)) {
286 Check if the memory address will be mapped by 4KB-page.
288 @param Address The address of Memory.
293 IN EFI_PHYSICAL_ADDRESS Address
298 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
300 // Check configuration
302 for (Index
= 0; Index
< mSplitMemRangeCount
; Index
++) {
303 if ((Address
>= mSplitMemRange
[Index
].Base
) && (Address
< mSplitMemRange
[Index
].Top
)) {
308 if (Address
< mCpuHotPlugData
.SmrrBase
) {
309 if ((mCpuHotPlugData
.SmrrBase
- Address
) < BASE_2MB
) {
312 } else if (Address
> (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
- BASE_2MB
)) {
313 if ((Address
- (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
- BASE_2MB
)) < BASE_2MB
) {
325 Initialize the protected memory ranges and the 4KB-page mapped memory ranges.
329 InitProtectedMemRange (
334 UINTN NumberOfDescriptors
;
335 UINTN NumberOfMmioDescriptors
;
336 UINTN NumberOfProtectRange
;
337 UINTN NumberOfSpliteRange
;
338 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
340 EFI_PHYSICAL_ADDRESS ProtectBaseAddress
;
341 EFI_PHYSICAL_ADDRESS ProtectEndAddress
;
342 EFI_PHYSICAL_ADDRESS Top2MBAlignedAddress
;
343 EFI_PHYSICAL_ADDRESS Base2MBAlignedAddress
;
344 UINT64 High4KBPageSize
;
345 UINT64 Low4KBPageSize
;
347 NumberOfDescriptors
= 0;
348 NumberOfMmioDescriptors
= 0;
349 NumberOfSpliteRange
= 0;
350 MemorySpaceMap
= NULL
;
353 // Get MMIO ranges from GCD and add them into protected memory ranges.
355 gDS
->GetMemorySpaceMap (
356 &NumberOfDescriptors
,
359 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
360 if (MemorySpaceMap
[Index
].GcdMemoryType
== EfiGcdMemoryTypeMemoryMappedIo
) {
361 NumberOfMmioDescriptors
++;
365 if (NumberOfMmioDescriptors
!= 0) {
366 TotalSize
= NumberOfMmioDescriptors
* sizeof (MEMORY_PROTECTION_RANGE
) + sizeof (mProtectionMemRangeTemplate
);
367 mProtectionMemRange
= (MEMORY_PROTECTION_RANGE
*) AllocateZeroPool (TotalSize
);
368 ASSERT (mProtectionMemRange
!= NULL
);
369 mProtectionMemRangeCount
= TotalSize
/ sizeof (MEMORY_PROTECTION_RANGE
);
372 // Copy existing ranges.
374 CopyMem (mProtectionMemRange
, mProtectionMemRangeTemplate
, sizeof (mProtectionMemRangeTemplate
));
377 // Create split ranges which come from protected ranges.
379 TotalSize
= (TotalSize
/ sizeof (MEMORY_PROTECTION_RANGE
)) * sizeof (MEMORY_RANGE
);
380 mSplitMemRange
= (MEMORY_RANGE
*) AllocateZeroPool (TotalSize
);
381 ASSERT (mSplitMemRange
!= NULL
);
384 // Create MMIO ranges which are set to present and execution-disable.
386 NumberOfProtectRange
= sizeof (mProtectionMemRangeTemplate
) / sizeof (MEMORY_PROTECTION_RANGE
);
387 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
388 if (MemorySpaceMap
[Index
].GcdMemoryType
!= EfiGcdMemoryTypeMemoryMappedIo
) {
391 mProtectionMemRange
[NumberOfProtectRange
].Range
.Base
= MemorySpaceMap
[Index
].BaseAddress
;
392 mProtectionMemRange
[NumberOfProtectRange
].Range
.Top
= MemorySpaceMap
[Index
].BaseAddress
+ MemorySpaceMap
[Index
].Length
;
393 mProtectionMemRange
[NumberOfProtectRange
].Present
= TRUE
;
394 mProtectionMemRange
[NumberOfProtectRange
].Nx
= TRUE
;
395 NumberOfProtectRange
++;
400 // According to protected ranges, create the ranges which will be mapped by 2KB page.
402 NumberOfSpliteRange
= 0;
403 NumberOfProtectRange
= mProtectionMemRangeCount
;
404 for (Index
= 0; Index
< NumberOfProtectRange
; Index
++) {
406 // If MMIO base address is not 2MB alignment, make 2MB alignment for create 4KB page in page table.
408 ProtectBaseAddress
= mProtectionMemRange
[Index
].Range
.Base
;
409 ProtectEndAddress
= mProtectionMemRange
[Index
].Range
.Top
;
410 if (((ProtectBaseAddress
& (SIZE_2MB
- 1)) != 0) || ((ProtectEndAddress
& (SIZE_2MB
- 1)) != 0)) {
412 // Check if it is possible to create 4KB-page for not 2MB-aligned range and to create 2MB-page for 2MB-aligned range.
413 // A mix of 4KB and 2MB page could save SMRAM space.
415 Top2MBAlignedAddress
= ProtectEndAddress
& ~(SIZE_2MB
- 1);
416 Base2MBAlignedAddress
= (ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
417 if ((Top2MBAlignedAddress
> Base2MBAlignedAddress
) &&
418 ((Top2MBAlignedAddress
- Base2MBAlignedAddress
) >= SIZE_2MB
)) {
420 // There is an range which could be mapped by 2MB-page.
422 High4KBPageSize
= ((ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1)) - (ProtectEndAddress
& ~(SIZE_2MB
- 1));
423 Low4KBPageSize
= ((ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1)) - (ProtectBaseAddress
& ~(SIZE_2MB
- 1));
424 if (High4KBPageSize
!= 0) {
426 // Add not 2MB-aligned range to be mapped by 4KB-page.
428 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectEndAddress
& ~(SIZE_2MB
- 1);
429 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
430 NumberOfSpliteRange
++;
432 if (Low4KBPageSize
!= 0) {
434 // Add not 2MB-aligned range to be mapped by 4KB-page.
436 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectBaseAddress
& ~(SIZE_2MB
- 1);
437 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
438 NumberOfSpliteRange
++;
442 // The range could only be mapped by 4KB-page.
444 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectBaseAddress
& ~(SIZE_2MB
- 1);
445 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
446 NumberOfSpliteRange
++;
451 mSplitMemRangeCount
= NumberOfSpliteRange
;
453 DEBUG ((EFI_D_INFO
, "SMM Profile Memory Ranges:\n"));
454 for (Index
= 0; Index
< mProtectionMemRangeCount
; Index
++) {
455 DEBUG ((EFI_D_INFO
, "mProtectionMemRange[%d].Base = %lx\n", Index
, mProtectionMemRange
[Index
].Range
.Base
));
456 DEBUG ((EFI_D_INFO
, "mProtectionMemRange[%d].Top = %lx\n", Index
, mProtectionMemRange
[Index
].Range
.Top
));
458 for (Index
= 0; Index
< mSplitMemRangeCount
; Index
++) {
459 DEBUG ((EFI_D_INFO
, "mSplitMemRange[%d].Base = %lx\n", Index
, mSplitMemRange
[Index
].Base
));
460 DEBUG ((EFI_D_INFO
, "mSplitMemRange[%d].Top = %lx\n", Index
, mSplitMemRange
[Index
].Top
));
465 Update page table according to protected memory ranges and the 4KB-page mapped memory ranges.
482 UINTN NumberOfPdpEntries
;
483 UINTN NumberOfPml4Entries
;
484 UINTN SizeOfMemorySpace
;
487 if (sizeof (UINTN
) == sizeof (UINT64
)) {
488 Pml4
= (UINT64
*)(UINTN
)mSmmProfileCr3
;
489 SizeOfMemorySpace
= HighBitSet64 (gPhyMask
) + 1;
491 // Calculate the table entries of PML4E and PDPTE.
493 if (SizeOfMemorySpace
<= 39 ) {
494 NumberOfPml4Entries
= 1;
495 NumberOfPdpEntries
= (UINT32
)LShiftU64 (1, (SizeOfMemorySpace
- 30));
497 NumberOfPml4Entries
= (UINT32
)LShiftU64 (1, (SizeOfMemorySpace
- 39));
498 NumberOfPdpEntries
= 512;
501 NumberOfPml4Entries
= 1;
502 NumberOfPdpEntries
= 4;
506 // Go through page table and change 2MB-page into 4KB-page.
508 for (Level1
= 0; Level1
< NumberOfPml4Entries
; Level1
++) {
509 if (sizeof (UINTN
) == sizeof (UINT64
)) {
510 if ((Pml4
[Level1
] & IA32_PG_P
) == 0) {
512 // If Pml4 entry does not exist, skip it
516 Pde
= (UINT64
*)(UINTN
)(Pml4
[Level1
] & PHYSICAL_ADDRESS_MASK
);
518 Pde
= (UINT64
*)(UINTN
)mSmmProfileCr3
;
520 for (Level2
= 0; Level2
< NumberOfPdpEntries
; Level2
++, Pde
++) {
521 if ((*Pde
& IA32_PG_P
) == 0) {
523 // If PDE entry does not exist, skip it
527 if ((*Pde
& IA32_PG_PS
) != 0) {
529 // This is 1G entry, skip it
533 Pte
= (UINT64
*)(UINTN
)(*Pde
& PHYSICAL_ADDRESS_MASK
);
537 for (Level3
= 0; Level3
< SIZE_4KB
/ sizeof (*Pte
); Level3
++, Pte
++) {
538 if ((*Pte
& IA32_PG_P
) == 0) {
540 // If PTE entry does not exist, skip it
544 Address
= (((Level2
<< 9) + Level3
) << 21);
547 // If it is 2M page, check IsAddressSplit()
549 if (((*Pte
& IA32_PG_PS
) != 0) && IsAddressSplit (Address
)) {
551 // Based on current page table, create 4KB page table for split area.
553 ASSERT (Address
== (*Pte
& PHYSICAL_ADDRESS_MASK
));
555 Pt
= AllocatePageTableMemory (1);
559 for (Level4
= 0; Level4
< SIZE_4KB
/ sizeof(*Pt
); Level4
++) {
560 Pt
[Level4
] = Address
+ ((Level4
<< 12) | PAGE_ATTRIBUTE_BITS
);
562 *Pte
= (UINTN
)Pt
| PAGE_ATTRIBUTE_BITS
;
563 } // end if IsAddressSplit
569 // Go through page table and set several page table entries to absent or execute-disable.
571 DEBUG ((EFI_D_INFO
, "Patch page table start ...\n"));
572 for (Level1
= 0; Level1
< NumberOfPml4Entries
; Level1
++) {
573 if (sizeof (UINTN
) == sizeof (UINT64
)) {
574 if ((Pml4
[Level1
] & IA32_PG_P
) == 0) {
576 // If Pml4 entry does not exist, skip it
580 Pde
= (UINT64
*)(UINTN
)(Pml4
[Level1
] & PHYSICAL_ADDRESS_MASK
);
582 Pde
= (UINT64
*)(UINTN
)mSmmProfileCr3
;
584 for (Level2
= 0; Level2
< NumberOfPdpEntries
; Level2
++, Pde
++) {
585 if ((*Pde
& IA32_PG_P
) == 0) {
587 // If PDE entry does not exist, skip it
591 if ((*Pde
& IA32_PG_PS
) != 0) {
593 // This is 1G entry, set NX bit and skip it
596 *Pde
= *Pde
| IA32_PG_NX
;
600 Pte
= (UINT64
*)(UINTN
)(*Pde
& PHYSICAL_ADDRESS_MASK
);
604 for (Level3
= 0; Level3
< SIZE_4KB
/ sizeof (*Pte
); Level3
++, Pte
++) {
605 if ((*Pte
& IA32_PG_P
) == 0) {
607 // If PTE entry does not exist, skip it
611 Address
= (((Level2
<< 9) + Level3
) << 21);
613 if ((*Pte
& IA32_PG_PS
) != 0) {
616 if (!IsAddressValid (Address
, &Nx
)) {
618 // Patch to remove Present flag and RW flag
620 *Pte
= *Pte
& (INTN
)(INT32
)(~PAGE_ATTRIBUTE_BITS
);
622 if (Nx
&& mXdSupported
) {
623 *Pte
= *Pte
| IA32_PG_NX
;
627 Pt
= (UINT64
*)(UINTN
)(*Pte
& PHYSICAL_ADDRESS_MASK
);
631 for (Level4
= 0; Level4
< SIZE_4KB
/ sizeof(*Pt
); Level4
++, Pt
++) {
632 if (!IsAddressValid (Address
, &Nx
)) {
633 *Pt
= *Pt
& (INTN
)(INT32
)(~PAGE_ATTRIBUTE_BITS
);
635 if (Nx
&& mXdSupported
) {
636 *Pt
= *Pt
| IA32_PG_NX
;
649 DEBUG ((EFI_D_INFO
, "Patch page table done!\n"));
651 // Set execute-disable flag
659 To find FADT in ACPI tables.
661 @param AcpiTableGuid The GUID used to find ACPI table in UEFI ConfigurationTable.
663 @return FADT table pointer.
665 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*
666 FindAcpiFadtTableByAcpiGuid (
667 IN EFI_GUID
*AcpiTableGuid
670 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
671 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
672 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
679 // found ACPI table RSD_PTR from system table
681 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
682 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
684 // A match was found.
686 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
695 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
696 if (Rsdt
== NULL
|| Rsdt
->Signature
!= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
700 for (Index
= sizeof (EFI_ACPI_DESCRIPTION_HEADER
); Index
< Rsdt
->Length
; Index
= Index
+ sizeof (UINT32
)) {
702 Data32
= *(UINT32
*) ((UINT8
*) Rsdt
+ Index
);
703 Fadt
= (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*) (UINT32
*) (UINTN
) Data32
;
704 if (Fadt
->Header
.Signature
== EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
) {
709 if (Fadt
== NULL
|| Fadt
->Header
.Signature
!= EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
) {
717 To find FADT in ACPI tables.
719 @return FADT table pointer.
721 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*
726 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
728 Fadt
= FindAcpiFadtTableByAcpiGuid (&gEfiAcpi20TableGuid
);
733 return FindAcpiFadtTableByAcpiGuid (&gEfiAcpi10TableGuid
);
737 To get system port address of the SMI Command Port in FADT table.
745 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
747 Fadt
= FindAcpiFadtTable ();
748 ASSERT (Fadt
!= NULL
);
750 mSmiCommandPort
= Fadt
->SmiCmd
;
751 DEBUG ((EFI_D_INFO
, "mSmiCommandPort = %x\n", mSmiCommandPort
));
755 Updates page table to make some memory ranges (like system memory) absent
756 and make some memory ranges (like MMIO) present and execute disable. It also
757 update 2MB-page to 4KB-page for some memory ranges.
766 // The flag indicates SMM profile starts to work.
768 mSmmProfileStart
= TRUE
;
772 Initialize SMM profile in SmmReadyToLock protocol callback function.
774 @param Protocol Points to the protocol's unique identifier.
775 @param Interface Points to the interface instance.
776 @param Handle The handle on which the interface was installed.
778 @retval EFI_SUCCESS SmmReadyToLock protocol callback runs successfully.
782 InitSmmProfileCallBack (
783 IN CONST EFI_GUID
*Protocol
,
789 // Save to variable so that SMM profile data can be found.
794 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
795 sizeof(mSmmProfileBase
),
800 // Get Software SMI from FADT
802 GetSmiCommandPort ();
805 // Initialize protected memory range for patching page table later.
807 InitProtectedMemRange ();
813 Initialize SMM profile data structures.
817 InitSmmProfileInternal (
822 EFI_PHYSICAL_ADDRESS Base
;
825 UINTN MsrDsAreaSizePerCpu
;
828 mPFEntryCount
= (UINTN
*)AllocateZeroPool (sizeof (UINTN
) * mMaxNumberOfCpus
);
829 ASSERT (mPFEntryCount
!= NULL
);
830 mLastPFEntryValue
= (UINT64 (*)[MAX_PF_ENTRY_COUNT
])AllocateZeroPool (
831 sizeof (mLastPFEntryValue
[0]) * mMaxNumberOfCpus
);
832 ASSERT (mLastPFEntryValue
!= NULL
);
833 mLastPFEntryPointer
= (UINT64
*(*)[MAX_PF_ENTRY_COUNT
])AllocateZeroPool (
834 sizeof (mLastPFEntryPointer
[0]) * mMaxNumberOfCpus
);
835 ASSERT (mLastPFEntryPointer
!= NULL
);
838 // Allocate memory for SmmProfile below 4GB.
841 mSmmProfileSize
= PcdGet32 (PcdCpuSmmProfileSize
);
842 ASSERT ((mSmmProfileSize
& 0xFFF) == 0);
845 TotalSize
= mSmmProfileSize
+ mMsrDsAreaSize
;
847 TotalSize
= mSmmProfileSize
;
851 Status
= gBS
->AllocatePages (
853 EfiReservedMemoryType
,
854 EFI_SIZE_TO_PAGES (TotalSize
),
857 ASSERT_EFI_ERROR (Status
);
858 ZeroMem ((VOID
*)(UINTN
)Base
, TotalSize
);
859 mSmmProfileBase
= (SMM_PROFILE_HEADER
*)(UINTN
)Base
;
862 // Initialize SMM profile data header.
864 mSmmProfileBase
->HeaderSize
= sizeof (SMM_PROFILE_HEADER
);
865 mSmmProfileBase
->MaxDataEntries
= (UINT64
)((mSmmProfileSize
- sizeof(SMM_PROFILE_HEADER
)) / sizeof (SMM_PROFILE_ENTRY
));
866 mSmmProfileBase
->MaxDataSize
= MultU64x64 (mSmmProfileBase
->MaxDataEntries
, sizeof(SMM_PROFILE_ENTRY
));
867 mSmmProfileBase
->CurDataEntries
= 0;
868 mSmmProfileBase
->CurDataSize
= 0;
869 mSmmProfileBase
->TsegStart
= mCpuHotPlugData
.SmrrBase
;
870 mSmmProfileBase
->TsegSize
= mCpuHotPlugData
.SmrrSize
;
871 mSmmProfileBase
->NumSmis
= 0;
872 mSmmProfileBase
->NumCpus
= gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
;
875 mMsrDsArea
= (MSR_DS_AREA_STRUCT
**)AllocateZeroPool (sizeof (MSR_DS_AREA_STRUCT
*) * mMaxNumberOfCpus
);
876 ASSERT (mMsrDsArea
!= NULL
);
877 mMsrBTSRecord
= (BRANCH_TRACE_RECORD
**)AllocateZeroPool (sizeof (BRANCH_TRACE_RECORD
*) * mMaxNumberOfCpus
);
878 ASSERT (mMsrBTSRecord
!= NULL
);
879 mMsrPEBSRecord
= (PEBS_RECORD
**)AllocateZeroPool (sizeof (PEBS_RECORD
*) * mMaxNumberOfCpus
);
880 ASSERT (mMsrPEBSRecord
!= NULL
);
882 mMsrDsAreaBase
= (MSR_DS_AREA_STRUCT
*)((UINTN
)Base
+ mSmmProfileSize
);
883 MsrDsAreaSizePerCpu
= mMsrDsAreaSize
/ mMaxNumberOfCpus
;
884 mBTSRecordNumber
= (MsrDsAreaSizePerCpu
- sizeof(PEBS_RECORD
) * PEBS_RECORD_NUMBER
- sizeof(MSR_DS_AREA_STRUCT
)) / sizeof(BRANCH_TRACE_RECORD
);
885 for (Index
= 0; Index
< mMaxNumberOfCpus
; Index
++) {
886 mMsrDsArea
[Index
] = (MSR_DS_AREA_STRUCT
*)((UINTN
)mMsrDsAreaBase
+ MsrDsAreaSizePerCpu
* Index
);
887 mMsrBTSRecord
[Index
] = (BRANCH_TRACE_RECORD
*)((UINTN
)mMsrDsArea
[Index
] + sizeof(MSR_DS_AREA_STRUCT
));
888 mMsrPEBSRecord
[Index
] = (PEBS_RECORD
*)((UINTN
)mMsrDsArea
[Index
] + MsrDsAreaSizePerCpu
- sizeof(PEBS_RECORD
) * PEBS_RECORD_NUMBER
);
890 mMsrDsArea
[Index
]->BTSBufferBase
= (UINTN
)mMsrBTSRecord
[Index
];
891 mMsrDsArea
[Index
]->BTSIndex
= mMsrDsArea
[Index
]->BTSBufferBase
;
892 mMsrDsArea
[Index
]->BTSAbsoluteMaximum
= mMsrDsArea
[Index
]->BTSBufferBase
+ mBTSRecordNumber
* sizeof(BRANCH_TRACE_RECORD
) + 1;
893 mMsrDsArea
[Index
]->BTSInterruptThreshold
= mMsrDsArea
[Index
]->BTSAbsoluteMaximum
+ 1;
895 mMsrDsArea
[Index
]->PEBSBufferBase
= (UINTN
)mMsrPEBSRecord
[Index
];
896 mMsrDsArea
[Index
]->PEBSIndex
= mMsrDsArea
[Index
]->PEBSBufferBase
;
897 mMsrDsArea
[Index
]->PEBSAbsoluteMaximum
= mMsrDsArea
[Index
]->PEBSBufferBase
+ PEBS_RECORD_NUMBER
* sizeof(PEBS_RECORD
) + 1;
898 mMsrDsArea
[Index
]->PEBSInterruptThreshold
= mMsrDsArea
[Index
]->PEBSAbsoluteMaximum
+ 1;
902 mProtectionMemRange
= mProtectionMemRangeTemplate
;
903 mProtectionMemRangeCount
= sizeof (mProtectionMemRangeTemplate
) / sizeof (MEMORY_PROTECTION_RANGE
);
906 // Update TSeg entry.
908 mProtectionMemRange
[0].Range
.Base
= mCpuHotPlugData
.SmrrBase
;
909 mProtectionMemRange
[0].Range
.Top
= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
;
912 // Update SMM profile entry.
914 mProtectionMemRange
[1].Range
.Base
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)mSmmProfileBase
;
915 mProtectionMemRange
[1].Range
.Top
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)mSmmProfileBase
+ TotalSize
;
918 // Allocate memory reserved for creating 4KB pages.
920 InitPagesForPFHandler ();
923 // Start SMM profile when SmmReadyToLock protocol is installed.
925 Status
= gSmst
->SmmRegisterProtocolNotify (
926 &gEfiSmmReadyToLockProtocolGuid
,
927 InitSmmProfileCallBack
,
930 ASSERT_EFI_ERROR (Status
);
936 Check if XD feature is supported by a processor.
940 CheckFeatureSupported (
946 MSR_IA32_MISC_ENABLE_REGISTER MiscEnableMsr
;
949 AsmCpuid (CPUID_EXTENDED_FUNCTION
, &RegEax
, NULL
, NULL
, NULL
);
950 if (RegEax
<= CPUID_EXTENDED_FUNCTION
) {
952 // Extended CPUID functions are not supported on this processor.
954 mXdSupported
= FALSE
;
957 AsmCpuid (CPUID_EXTENDED_CPU_SIG
, NULL
, NULL
, NULL
, &RegEdx
);
958 if ((RegEdx
& CPUID1_EDX_XD_SUPPORT
) == 0) {
960 // Execute Disable Bit feature is not supported on this processor.
962 mXdSupported
= FALSE
;
967 AsmCpuid (CPUID_VERSION_INFO
, NULL
, NULL
, NULL
, &RegEdx
);
968 if ((RegEdx
& CPUID1_EDX_BTS_AVAILABLE
) != 0) {
971 // When CPUID.1:EDX[21] is set, the following BTS facilities are available:
972 // 1. The BTS_UNAVAILABLE flag in the IA32_MISC_ENABLE MSR indicates the
973 // availability of the BTS facilities, including the ability to set the BTS and
974 // BTINT bits in the MSR_DEBUGCTLA MSR.
975 // 2. The IA32_DS_AREA MSR can be programmed to point to the DS save area.
977 MiscEnableMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_MISC_ENABLE
);
978 if (MiscEnableMsr
.Bits
.BTS
== 1) {
980 // BTS facilities is not supported if MSR_IA32_MISC_ENABLE.BTS bit is set.
982 mBtsSupported
= FALSE
;
993 ActivateSingleStepDB (
1000 if ((Dr6
& DR6_SINGLE_STEP
) != 0) {
1003 Dr6
|= DR6_SINGLE_STEP
;
1018 DebugCtl
= AsmReadMsr64 (MSR_DEBUG_CTL
);
1019 if ((DebugCtl
& MSR_DEBUG_CTL_LBR
) != 0) {
1022 DebugCtl
|= MSR_DEBUG_CTL_LBR
;
1023 AsmWriteMsr64 (MSR_DEBUG_CTL
, DebugCtl
);
1027 Enable branch trace store.
1029 @param CpuIndex The index of the processor.
1039 DebugCtl
= AsmReadMsr64 (MSR_DEBUG_CTL
);
1040 if ((DebugCtl
& MSR_DEBUG_CTL_BTS
) != 0) {
1044 AsmWriteMsr64 (MSR_DS_AREA
, (UINT64
)(UINTN
)mMsrDsArea
[CpuIndex
]);
1045 DebugCtl
|= (UINT64
)(MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
);
1046 DebugCtl
&= ~((UINT64
)MSR_DEBUG_CTL_BTINT
);
1047 AsmWriteMsr64 (MSR_DEBUG_CTL
, DebugCtl
);
1051 Increase SMI number in each SMI entry.
1055 SmmProfileRecordSmiNum (
1059 if (mSmmProfileStart
) {
1060 mSmmProfileBase
->NumSmis
++;
1065 Initialize processor environment for SMM profile.
1067 @param CpuIndex The index of the processor.
1071 ActivateSmmProfile (
1076 // Enable Single Step DB#
1078 ActivateSingleStepDB ();
1080 if (mBtsSupported
) {
1082 // We can not get useful information from LER, so we have to use BTS.
1089 ActivateBTS (CpuIndex
);
1094 Initialize SMM profile in SMM CPU entry point.
1096 @param[in] Cr3 The base address of the page tables to use in SMM.
1107 mSmmProfileCr3
= Cr3
;
1110 // Skip SMM profile initialization if feature is disabled
1112 if (!FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
1117 // Initialize SmmProfile here
1119 InitSmmProfileInternal ();
1122 // Initialize profile IDT.
1128 Update page table to map the memory correctly in order to make the instruction
1129 which caused page fault execute successfully. And it also save the original page
1130 table to be restored in single-step exception.
1132 @param PageTable PageTable Address.
1133 @param PFAddress The memory address which caused page fault exception.
1134 @param CpuIndex The index of the processor.
1135 @param ErrorCode The Error code of exception.
1139 RestorePageTableBelow4G (
1152 if (sizeof(UINT64
) == sizeof(UINTN
)) {
1153 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 39, 47);
1154 ASSERT (PageTable
[PTIndex
] != 0);
1155 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1161 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 30, 38);
1162 ASSERT (PageTable
[PTIndex
] != 0);
1163 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1168 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 21, 29);
1169 if ((PageTable
[PTIndex
] & IA32_PG_PS
) != 0) {
1175 // Record old entries with non-present status
1176 // Old entries include the memory which instruction is at and the memory which instruction access.
1179 ASSERT (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
);
1180 if (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
) {
1181 PFIndex
= mPFEntryCount
[CpuIndex
];
1182 mLastPFEntryValue
[CpuIndex
][PFIndex
] = PageTable
[PTIndex
];
1183 mLastPFEntryPointer
[CpuIndex
][PFIndex
] = &PageTable
[PTIndex
];
1184 mPFEntryCount
[CpuIndex
]++;
1190 PageTable
[PTIndex
] = (PFAddress
& ~((1ull << 21) - 1));
1191 PageTable
[PTIndex
] |= (UINT64
)IA32_PG_PS
;
1192 PageTable
[PTIndex
] |= (UINT64
)PAGE_ATTRIBUTE_BITS
;
1193 if ((ErrorCode
& IA32_PF_EC_ID
) != 0) {
1194 PageTable
[PTIndex
] &= ~IA32_PG_NX
;
1200 ASSERT (PageTable
[PTIndex
] != 0);
1201 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1206 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 12, 20);
1209 // Record old entries with non-present status
1210 // Old entries include the memory which instruction is at and the memory which instruction access.
1213 ASSERT (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
);
1214 if (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
) {
1215 PFIndex
= mPFEntryCount
[CpuIndex
];
1216 mLastPFEntryValue
[CpuIndex
][PFIndex
] = PageTable
[PTIndex
];
1217 mLastPFEntryPointer
[CpuIndex
][PFIndex
] = &PageTable
[PTIndex
];
1218 mPFEntryCount
[CpuIndex
]++;
1224 PageTable
[PTIndex
] = (PFAddress
& ~((1ull << 12) - 1));
1225 PageTable
[PTIndex
] |= (UINT64
)PAGE_ATTRIBUTE_BITS
;
1226 if ((ErrorCode
& IA32_PF_EC_ID
) != 0) {
1227 PageTable
[PTIndex
] &= ~IA32_PG_NX
;
1233 The Page fault handler to save SMM profile data.
1235 @param Rip The RIP when exception happens.
1236 @param ErrorCode The Error code of exception.
1240 SmmProfilePFHandler (
1249 UINT64 InstructionAddress
;
1250 UINTN MaxEntryNumber
;
1251 UINTN CurrentEntryNumber
;
1252 BOOLEAN IsValidPFAddress
;
1253 SMM_PROFILE_ENTRY
*SmmProfileEntry
;
1257 EFI_SMM_SAVE_STATE_IO_INFO IoInfo
;
1259 if (!mSmmProfileStart
) {
1261 // If SMM profile does not start, call original page fault handler.
1263 SmiDefaultPFHandler ();
1267 if (mBtsSupported
) {
1271 IsValidPFAddress
= FALSE
;
1272 PageTable
= (UINT64
*)AsmReadCr3 ();
1273 PFAddress
= AsmReadCr2 ();
1274 CpuIndex
= GetCpuIndex ();
1276 if (PFAddress
<= 0xFFFFFFFF) {
1277 RestorePageTableBelow4G (PageTable
, PFAddress
, CpuIndex
, ErrorCode
);
1279 RestorePageTableAbove4G (PageTable
, PFAddress
, CpuIndex
, ErrorCode
, &IsValidPFAddress
);
1282 if (!IsValidPFAddress
) {
1283 InstructionAddress
= Rip
;
1284 if ((ErrorCode
& IA32_PF_EC_ID
) != 0 && (mBtsSupported
)) {
1286 // If it is instruction fetch failure, get the correct IP from BTS.
1288 InstructionAddress
= GetSourceFromDestinationOnBts (CpuIndex
, Rip
);
1289 if (InstructionAddress
== 0) {
1291 // It indicates the instruction which caused page fault is not a jump instruction,
1292 // set instruction address same as the page fault address.
1294 InstructionAddress
= PFAddress
;
1299 // Indicate it is not software SMI
1301 SmiCommand
= 0xFFFFFFFFFFFFFFFFULL
;
1302 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
1303 Status
= SmmReadSaveState(&mSmmCpu
, sizeof(IoInfo
), EFI_SMM_SAVE_STATE_REGISTER_IO
, Index
, &IoInfo
);
1304 if (EFI_ERROR (Status
)) {
1307 if (IoInfo
.IoPort
== mSmiCommandPort
) {
1309 // A software SMI triggered by SMI command port has been found, get SmiCommand from SMI command port.
1311 SoftSmiValue
= IoRead8 (mSmiCommandPort
);
1312 SmiCommand
= (UINT64
)SoftSmiValue
;
1317 SmmProfileEntry
= (SMM_PROFILE_ENTRY
*)(UINTN
)(mSmmProfileBase
+ 1);
1319 // Check if there is already a same entry in profile data.
1321 for (Index
= 0; Index
< (UINTN
) mSmmProfileBase
->CurDataEntries
; Index
++) {
1322 if ((SmmProfileEntry
[Index
].ErrorCode
== (UINT64
)ErrorCode
) &&
1323 (SmmProfileEntry
[Index
].Address
== PFAddress
) &&
1324 (SmmProfileEntry
[Index
].CpuNum
== (UINT64
)CpuIndex
) &&
1325 (SmmProfileEntry
[Index
].Instruction
== InstructionAddress
) &&
1326 (SmmProfileEntry
[Index
].SmiCmd
== SmiCommand
)) {
1328 // Same record exist, need not save again.
1333 if (Index
== mSmmProfileBase
->CurDataEntries
) {
1334 CurrentEntryNumber
= (UINTN
) mSmmProfileBase
->CurDataEntries
;
1335 MaxEntryNumber
= (UINTN
) mSmmProfileBase
->MaxDataEntries
;
1336 if (FeaturePcdGet (PcdCpuSmmProfileRingBuffer
)) {
1337 CurrentEntryNumber
= CurrentEntryNumber
% MaxEntryNumber
;
1339 if (CurrentEntryNumber
< MaxEntryNumber
) {
1341 // Log the new entry
1343 SmmProfileEntry
[CurrentEntryNumber
].SmiNum
= mSmmProfileBase
->NumSmis
;
1344 SmmProfileEntry
[CurrentEntryNumber
].ErrorCode
= (UINT64
)ErrorCode
;
1345 SmmProfileEntry
[CurrentEntryNumber
].ApicId
= (UINT64
)GetApicId ();
1346 SmmProfileEntry
[CurrentEntryNumber
].CpuNum
= (UINT64
)CpuIndex
;
1347 SmmProfileEntry
[CurrentEntryNumber
].Address
= PFAddress
;
1348 SmmProfileEntry
[CurrentEntryNumber
].Instruction
= InstructionAddress
;
1349 SmmProfileEntry
[CurrentEntryNumber
].SmiCmd
= SmiCommand
;
1351 // Update current entry index and data size in the header.
1353 mSmmProfileBase
->CurDataEntries
++;
1354 mSmmProfileBase
->CurDataSize
= MultU64x64 (mSmmProfileBase
->CurDataEntries
, sizeof (SMM_PROFILE_ENTRY
));
1363 if (mBtsSupported
) {
1369 Replace INT1 exception handler to restore page table to absent/execute-disable state
1370 in order to trigger page fault again to save SMM profile data..
1380 Status
= SmmRegisterExceptionHandler (&mSmmCpuService
, EXCEPT_IA32_DEBUG
, DebugExceptionHandler
);
1381 ASSERT_EFI_ERROR (Status
);