4 Copyright (c) 2012 - 2017, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
7 This program and the accompanying materials
8 are licensed and made available under the terms and conditions of the BSD License
9 which accompanies this distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include "PiSmmCpuDxeSmm.h"
18 #include "SmmProfileInternal.h"
20 UINT32 mSmmProfileCr3
;
22 SMM_PROFILE_HEADER
*mSmmProfileBase
;
23 MSR_DS_AREA_STRUCT
*mMsrDsAreaBase
;
25 // The buffer to store SMM profile data.
27 UINTN mSmmProfileSize
;
30 // The buffer to enable branch trace store.
32 UINTN mMsrDsAreaSize
= SMM_PROFILE_DTS_SIZE
;
35 // The flag indicates if execute-disable is supported by processor.
37 BOOLEAN mXdSupported
= TRUE
;
40 // The flag indicates if execute-disable is enabled on processor.
42 BOOLEAN mXdEnabled
= FALSE
;
45 // The flag indicates if BTS is supported by processor.
47 BOOLEAN mBtsSupported
= TRUE
;
50 // The flag indicates if SMM profile starts to record data.
52 BOOLEAN mSmmProfileStart
= FALSE
;
55 // Record the page fault exception count for one instruction execution.
59 UINT64 (*mLastPFEntryValue
)[MAX_PF_ENTRY_COUNT
];
60 UINT64
*(*mLastPFEntryPointer
)[MAX_PF_ENTRY_COUNT
];
62 MSR_DS_AREA_STRUCT
**mMsrDsArea
;
63 BRANCH_TRACE_RECORD
**mMsrBTSRecord
;
64 UINTN mBTSRecordNumber
;
65 PEBS_RECORD
**mMsrPEBSRecord
;
68 // These memory ranges are always present, they does not generate the access type of page fault exception,
69 // but they possibly generate instruction fetch type of page fault exception.
71 MEMORY_PROTECTION_RANGE
*mProtectionMemRange
= NULL
;
72 UINTN mProtectionMemRangeCount
= 0;
75 // Some predefined memory ranges.
77 MEMORY_PROTECTION_RANGE mProtectionMemRangeTemplate
[] = {
79 // SMRAM range (to be fixed in runtime).
80 // It is always present and instruction fetches are allowed.
82 {{0x00000000, 0x00000000},TRUE
,FALSE
},
85 // SMM profile data range( to be fixed in runtime).
86 // It is always present and instruction fetches are not allowed.
88 {{0x00000000, 0x00000000},TRUE
,TRUE
},
91 // SMRAM ranges not covered by mCpuHotPlugData.SmrrBase/mCpuHotPlugData.SmrrSiz (to be fixed in runtime).
92 // It is always present and instruction fetches are allowed.
93 // {{0x00000000, 0x00000000},TRUE,FALSE},
97 // Future extended range could be added here.
101 // PCI MMIO ranges (to be added in runtime).
102 // They are always present and instruction fetches are not allowed.
107 // These memory ranges are mapped by 4KB-page instead of 2MB-page.
109 MEMORY_RANGE
*mSplitMemRange
= NULL
;
110 UINTN mSplitMemRangeCount
= 0;
115 UINT32 mSmiCommandPort
;
118 Disable branch trace store.
126 AsmMsrAnd64 (MSR_DEBUG_CTL
, ~((UINT64
)(MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
)));
130 Enable branch trace store.
138 AsmMsrOr64 (MSR_DEBUG_CTL
, (MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
));
142 Get CPU Index from APIC ID.
153 ApicId
= GetApicId ();
155 for (Index
= 0; Index
< mMaxNumberOfCpus
; Index
++) {
156 if (gSmmCpuPrivate
->ProcessorInfo
[Index
].ProcessorId
== ApicId
) {
165 Get the source of IP after execute-disable exception is triggered.
167 @param CpuIndex The index of CPU.
168 @param DestinationIP The destination address.
172 GetSourceFromDestinationOnBts (
177 BRANCH_TRACE_RECORD
*CurrentBTSRecord
;
183 CurrentBTSRecord
= (BRANCH_TRACE_RECORD
*)mMsrDsArea
[CpuIndex
]->BTSIndex
;
184 for (Index
= 0; Index
< mBTSRecordNumber
; Index
++) {
185 if ((UINTN
)CurrentBTSRecord
< (UINTN
)mMsrBTSRecord
[CpuIndex
]) {
189 CurrentBTSRecord
= (BRANCH_TRACE_RECORD
*)((UINTN
)mMsrDsArea
[CpuIndex
]->BTSAbsoluteMaximum
- 1);
192 if (CurrentBTSRecord
->LastBranchTo
== DestinationIP
) {
194 // Good! find 1st one, then find 2nd one.
198 // The first one is DEBUG exception
203 // Good find proper one.
205 return CurrentBTSRecord
->LastBranchFrom
;
215 SMM profile specific INT 1 (single-step) exception handler.
217 @param InterruptType Defines the type of interrupt or exception that
218 occurred on the processor.This parameter is processor architecture specific.
219 @param SystemContext A pointer to the processor context when
220 the interrupt occurred on the processor.
224 DebugExceptionHandler (
225 IN EFI_EXCEPTION_TYPE InterruptType
,
226 IN EFI_SYSTEM_CONTEXT SystemContext
232 if (!mSmmProfileStart
) {
235 CpuIndex
= GetCpuIndex ();
238 // Clear last PF entries
240 for (PFEntry
= 0; PFEntry
< mPFEntryCount
[CpuIndex
]; PFEntry
++) {
241 *mLastPFEntryPointer
[CpuIndex
][PFEntry
] = mLastPFEntryValue
[CpuIndex
][PFEntry
];
245 // Reset page fault exception count for next page fault.
247 mPFEntryCount
[CpuIndex
] = 0;
255 // Clear TF in EFLAGS
257 ClearTrapFlag (SystemContext
);
261 Check if the input address is in SMM ranges.
263 @param[in] Address The input address.
265 @retval TRUE The input address is in SMM.
266 @retval FALSE The input address is not in SMM.
270 IN EFI_PHYSICAL_ADDRESS Address
275 if ((Address
>= mCpuHotPlugData
.SmrrBase
) && (Address
< mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
)) {
278 for (Index
= 0; Index
< mSmmCpuSmramRangeCount
; Index
++) {
279 if (Address
>= mSmmCpuSmramRanges
[Index
].CpuStart
&&
280 Address
< mSmmCpuSmramRanges
[Index
].CpuStart
+ mSmmCpuSmramRanges
[Index
].PhysicalSize
) {
288 Check if the memory address will be mapped by 4KB-page.
290 @param Address The address of Memory.
291 @param Nx The flag indicates if the memory is execute-disable.
296 IN EFI_PHYSICAL_ADDRESS Address
,
302 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
304 // Check configuration
306 for (Index
= 0; Index
< mProtectionMemRangeCount
; Index
++) {
307 if ((Address
>= mProtectionMemRange
[Index
].Range
.Base
) && (Address
< mProtectionMemRange
[Index
].Range
.Top
)) {
308 *Nx
= mProtectionMemRange
[Index
].Nx
;
309 return mProtectionMemRange
[Index
].Present
;
317 if (IsInSmmRanges (Address
)) {
325 Check if the memory address will be mapped by 4KB-page.
327 @param Address The address of Memory.
332 IN EFI_PHYSICAL_ADDRESS Address
337 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
339 // Check configuration
341 for (Index
= 0; Index
< mSplitMemRangeCount
; Index
++) {
342 if ((Address
>= mSplitMemRange
[Index
].Base
) && (Address
< mSplitMemRange
[Index
].Top
)) {
347 if (Address
< mCpuHotPlugData
.SmrrBase
) {
348 if ((mCpuHotPlugData
.SmrrBase
- Address
) < BASE_2MB
) {
351 } else if (Address
> (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
- BASE_2MB
)) {
352 if ((Address
- (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
- BASE_2MB
)) < BASE_2MB
) {
364 Initialize the protected memory ranges and the 4KB-page mapped memory ranges.
368 InitProtectedMemRange (
373 UINTN NumberOfDescriptors
;
374 UINTN NumberOfAddedDescriptors
;
375 UINTN NumberOfProtectRange
;
376 UINTN NumberOfSpliteRange
;
377 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
379 EFI_PHYSICAL_ADDRESS ProtectBaseAddress
;
380 EFI_PHYSICAL_ADDRESS ProtectEndAddress
;
381 EFI_PHYSICAL_ADDRESS Top2MBAlignedAddress
;
382 EFI_PHYSICAL_ADDRESS Base2MBAlignedAddress
;
383 UINT64 High4KBPageSize
;
384 UINT64 Low4KBPageSize
;
386 NumberOfDescriptors
= 0;
387 NumberOfAddedDescriptors
= mSmmCpuSmramRangeCount
;
388 NumberOfSpliteRange
= 0;
389 MemorySpaceMap
= NULL
;
392 // Get MMIO ranges from GCD and add them into protected memory ranges.
394 gDS
->GetMemorySpaceMap (
395 &NumberOfDescriptors
,
398 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
399 if (MemorySpaceMap
[Index
].GcdMemoryType
== EfiGcdMemoryTypeMemoryMappedIo
) {
400 NumberOfAddedDescriptors
++;
404 if (NumberOfAddedDescriptors
!= 0) {
405 TotalSize
= NumberOfAddedDescriptors
* sizeof (MEMORY_PROTECTION_RANGE
) + sizeof (mProtectionMemRangeTemplate
);
406 mProtectionMemRange
= (MEMORY_PROTECTION_RANGE
*) AllocateZeroPool (TotalSize
);
407 ASSERT (mProtectionMemRange
!= NULL
);
408 mProtectionMemRangeCount
= TotalSize
/ sizeof (MEMORY_PROTECTION_RANGE
);
411 // Copy existing ranges.
413 CopyMem (mProtectionMemRange
, mProtectionMemRangeTemplate
, sizeof (mProtectionMemRangeTemplate
));
416 // Create split ranges which come from protected ranges.
418 TotalSize
= (TotalSize
/ sizeof (MEMORY_PROTECTION_RANGE
)) * sizeof (MEMORY_RANGE
);
419 mSplitMemRange
= (MEMORY_RANGE
*) AllocateZeroPool (TotalSize
);
420 ASSERT (mSplitMemRange
!= NULL
);
423 // Create SMM ranges which are set to present and execution-enable.
425 NumberOfProtectRange
= sizeof (mProtectionMemRangeTemplate
) / sizeof (MEMORY_PROTECTION_RANGE
);
426 for (Index
= 0; Index
< mSmmCpuSmramRangeCount
; Index
++) {
427 if (mSmmCpuSmramRanges
[Index
].CpuStart
>= mProtectionMemRange
[0].Range
.Base
&&
428 mSmmCpuSmramRanges
[Index
].CpuStart
+ mSmmCpuSmramRanges
[Index
].PhysicalSize
< mProtectionMemRange
[0].Range
.Top
) {
430 // If the address have been already covered by mCpuHotPlugData.SmrrBase/mCpuHotPlugData.SmrrSiz
434 mProtectionMemRange
[NumberOfProtectRange
].Range
.Base
= mSmmCpuSmramRanges
[Index
].CpuStart
;
435 mProtectionMemRange
[NumberOfProtectRange
].Range
.Top
= mSmmCpuSmramRanges
[Index
].CpuStart
+ mSmmCpuSmramRanges
[Index
].PhysicalSize
;
436 mProtectionMemRange
[NumberOfProtectRange
].Present
= TRUE
;
437 mProtectionMemRange
[NumberOfProtectRange
].Nx
= FALSE
;
438 NumberOfProtectRange
++;
442 // Create MMIO ranges which are set to present and execution-disable.
444 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
445 if (MemorySpaceMap
[Index
].GcdMemoryType
!= EfiGcdMemoryTypeMemoryMappedIo
) {
448 mProtectionMemRange
[NumberOfProtectRange
].Range
.Base
= MemorySpaceMap
[Index
].BaseAddress
;
449 mProtectionMemRange
[NumberOfProtectRange
].Range
.Top
= MemorySpaceMap
[Index
].BaseAddress
+ MemorySpaceMap
[Index
].Length
;
450 mProtectionMemRange
[NumberOfProtectRange
].Present
= TRUE
;
451 mProtectionMemRange
[NumberOfProtectRange
].Nx
= TRUE
;
452 NumberOfProtectRange
++;
456 // Check and updated actual protected memory ranges count
458 ASSERT (NumberOfProtectRange
<= mProtectionMemRangeCount
);
459 mProtectionMemRangeCount
= NumberOfProtectRange
;
463 // According to protected ranges, create the ranges which will be mapped by 2KB page.
465 NumberOfSpliteRange
= 0;
466 NumberOfProtectRange
= mProtectionMemRangeCount
;
467 for (Index
= 0; Index
< NumberOfProtectRange
; Index
++) {
469 // If MMIO base address is not 2MB alignment, make 2MB alignment for create 4KB page in page table.
471 ProtectBaseAddress
= mProtectionMemRange
[Index
].Range
.Base
;
472 ProtectEndAddress
= mProtectionMemRange
[Index
].Range
.Top
;
473 if (((ProtectBaseAddress
& (SIZE_2MB
- 1)) != 0) || ((ProtectEndAddress
& (SIZE_2MB
- 1)) != 0)) {
475 // Check if it is possible to create 4KB-page for not 2MB-aligned range and to create 2MB-page for 2MB-aligned range.
476 // A mix of 4KB and 2MB page could save SMRAM space.
478 Top2MBAlignedAddress
= ProtectEndAddress
& ~(SIZE_2MB
- 1);
479 Base2MBAlignedAddress
= (ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
480 if ((Top2MBAlignedAddress
> Base2MBAlignedAddress
) &&
481 ((Top2MBAlignedAddress
- Base2MBAlignedAddress
) >= SIZE_2MB
)) {
483 // There is an range which could be mapped by 2MB-page.
485 High4KBPageSize
= ((ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1)) - (ProtectEndAddress
& ~(SIZE_2MB
- 1));
486 Low4KBPageSize
= ((ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1)) - (ProtectBaseAddress
& ~(SIZE_2MB
- 1));
487 if (High4KBPageSize
!= 0) {
489 // Add not 2MB-aligned range to be mapped by 4KB-page.
491 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectEndAddress
& ~(SIZE_2MB
- 1);
492 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
493 NumberOfSpliteRange
++;
495 if (Low4KBPageSize
!= 0) {
497 // Add not 2MB-aligned range to be mapped by 4KB-page.
499 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectBaseAddress
& ~(SIZE_2MB
- 1);
500 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectBaseAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
501 NumberOfSpliteRange
++;
505 // The range could only be mapped by 4KB-page.
507 mSplitMemRange
[NumberOfSpliteRange
].Base
= ProtectBaseAddress
& ~(SIZE_2MB
- 1);
508 mSplitMemRange
[NumberOfSpliteRange
].Top
= (ProtectEndAddress
+ SIZE_2MB
- 1) & ~(SIZE_2MB
- 1);
509 NumberOfSpliteRange
++;
514 mSplitMemRangeCount
= NumberOfSpliteRange
;
516 DEBUG ((EFI_D_INFO
, "SMM Profile Memory Ranges:\n"));
517 for (Index
= 0; Index
< mProtectionMemRangeCount
; Index
++) {
518 DEBUG ((EFI_D_INFO
, "mProtectionMemRange[%d].Base = %lx\n", Index
, mProtectionMemRange
[Index
].Range
.Base
));
519 DEBUG ((EFI_D_INFO
, "mProtectionMemRange[%d].Top = %lx\n", Index
, mProtectionMemRange
[Index
].Range
.Top
));
521 for (Index
= 0; Index
< mSplitMemRangeCount
; Index
++) {
522 DEBUG ((EFI_D_INFO
, "mSplitMemRange[%d].Base = %lx\n", Index
, mSplitMemRange
[Index
].Base
));
523 DEBUG ((EFI_D_INFO
, "mSplitMemRange[%d].Top = %lx\n", Index
, mSplitMemRange
[Index
].Top
));
528 Update page table according to protected memory ranges and the 4KB-page mapped memory ranges.
545 UINTN NumberOfPdpEntries
;
546 UINTN NumberOfPml4Entries
;
547 UINTN SizeOfMemorySpace
;
550 if (sizeof (UINTN
) == sizeof (UINT64
)) {
551 Pml4
= (UINT64
*)(UINTN
)mSmmProfileCr3
;
552 SizeOfMemorySpace
= HighBitSet64 (gPhyMask
) + 1;
554 // Calculate the table entries of PML4E and PDPTE.
556 if (SizeOfMemorySpace
<= 39 ) {
557 NumberOfPml4Entries
= 1;
558 NumberOfPdpEntries
= (UINT32
)LShiftU64 (1, (SizeOfMemorySpace
- 30));
560 NumberOfPml4Entries
= (UINT32
)LShiftU64 (1, (SizeOfMemorySpace
- 39));
561 NumberOfPdpEntries
= 512;
564 NumberOfPml4Entries
= 1;
565 NumberOfPdpEntries
= 4;
569 // Go through page table and change 2MB-page into 4KB-page.
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
] & ~mAddressEncMask
& 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 if ((*Pde
& IA32_PG_PS
) != 0) {
592 // This is 1G entry, skip it
596 Pte
= (UINT64
*)(UINTN
)(*Pde
& ~mAddressEncMask
& PHYSICAL_ADDRESS_MASK
);
600 for (Level3
= 0; Level3
< SIZE_4KB
/ sizeof (*Pte
); Level3
++, Pte
++) {
601 if ((*Pte
& IA32_PG_P
) == 0) {
603 // If PTE entry does not exist, skip it
607 Address
= (((Level2
<< 9) + Level3
) << 21);
610 // If it is 2M page, check IsAddressSplit()
612 if (((*Pte
& IA32_PG_PS
) != 0) && IsAddressSplit (Address
)) {
614 // Based on current page table, create 4KB page table for split area.
616 ASSERT (Address
== (*Pte
& PHYSICAL_ADDRESS_MASK
));
618 Pt
= AllocatePageTableMemory (1);
622 for (Level4
= 0; Level4
< SIZE_4KB
/ sizeof(*Pt
); Level4
++) {
623 Pt
[Level4
] = Address
+ ((Level4
<< 12) | mAddressEncMask
| PAGE_ATTRIBUTE_BITS
);
625 *Pte
= (UINT64
)(UINTN
)Pt
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
626 } // end if IsAddressSplit
632 // Go through page table and set several page table entries to absent or execute-disable.
634 DEBUG ((EFI_D_INFO
, "Patch page table start ...\n"));
635 for (Level1
= 0; Level1
< NumberOfPml4Entries
; Level1
++) {
636 if (sizeof (UINTN
) == sizeof (UINT64
)) {
637 if ((Pml4
[Level1
] & IA32_PG_P
) == 0) {
639 // If Pml4 entry does not exist, skip it
643 Pde
= (UINT64
*)(UINTN
)(Pml4
[Level1
] & ~mAddressEncMask
& PHYSICAL_ADDRESS_MASK
);
645 Pde
= (UINT64
*)(UINTN
)mSmmProfileCr3
;
647 for (Level2
= 0; Level2
< NumberOfPdpEntries
; Level2
++, Pde
++) {
648 if ((*Pde
& IA32_PG_P
) == 0) {
650 // If PDE entry does not exist, skip it
654 if ((*Pde
& IA32_PG_PS
) != 0) {
656 // This is 1G entry, set NX bit and skip it
659 *Pde
= *Pde
| IA32_PG_NX
;
663 Pte
= (UINT64
*)(UINTN
)(*Pde
& ~mAddressEncMask
& PHYSICAL_ADDRESS_MASK
);
667 for (Level3
= 0; Level3
< SIZE_4KB
/ sizeof (*Pte
); Level3
++, Pte
++) {
668 if ((*Pte
& IA32_PG_P
) == 0) {
670 // If PTE entry does not exist, skip it
674 Address
= (((Level2
<< 9) + Level3
) << 21);
676 if ((*Pte
& IA32_PG_PS
) != 0) {
679 if (!IsAddressValid (Address
, &Nx
)) {
681 // Patch to remove Present flag and RW flag
683 *Pte
= *Pte
& (INTN
)(INT32
)(~PAGE_ATTRIBUTE_BITS
);
685 if (Nx
&& mXdSupported
) {
686 *Pte
= *Pte
| IA32_PG_NX
;
690 Pt
= (UINT64
*)(UINTN
)(*Pte
& ~mAddressEncMask
& PHYSICAL_ADDRESS_MASK
);
694 for (Level4
= 0; Level4
< SIZE_4KB
/ sizeof(*Pt
); Level4
++, Pt
++) {
695 if (!IsAddressValid (Address
, &Nx
)) {
696 *Pt
= *Pt
& (INTN
)(INT32
)(~PAGE_ATTRIBUTE_BITS
);
698 if (Nx
&& mXdSupported
) {
699 *Pt
= *Pt
| IA32_PG_NX
;
712 DEBUG ((EFI_D_INFO
, "Patch page table done!\n"));
714 // Set execute-disable flag
722 To find FADT in ACPI tables.
724 @param AcpiTableGuid The GUID used to find ACPI table in UEFI ConfigurationTable.
726 @return FADT table pointer.
728 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*
729 FindAcpiFadtTableByAcpiGuid (
730 IN EFI_GUID
*AcpiTableGuid
733 EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_POINTER
*Rsdp
;
734 EFI_ACPI_DESCRIPTION_HEADER
*Rsdt
;
735 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
742 // found ACPI table RSD_PTR from system table
744 for (Index
= 0; Index
< gST
->NumberOfTableEntries
; Index
++) {
745 if (CompareGuid (&(gST
->ConfigurationTable
[Index
].VendorGuid
), AcpiTableGuid
)) {
747 // A match was found.
749 Rsdp
= gST
->ConfigurationTable
[Index
].VendorTable
;
758 Rsdt
= (EFI_ACPI_DESCRIPTION_HEADER
*)(UINTN
) Rsdp
->RsdtAddress
;
759 if (Rsdt
== NULL
|| Rsdt
->Signature
!= EFI_ACPI_2_0_ROOT_SYSTEM_DESCRIPTION_TABLE_SIGNATURE
) {
763 for (Index
= sizeof (EFI_ACPI_DESCRIPTION_HEADER
); Index
< Rsdt
->Length
; Index
= Index
+ sizeof (UINT32
)) {
765 Data32
= *(UINT32
*) ((UINT8
*) Rsdt
+ Index
);
766 Fadt
= (EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*) (UINT32
*) (UINTN
) Data32
;
767 if (Fadt
->Header
.Signature
== EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
) {
772 if (Fadt
== NULL
|| Fadt
->Header
.Signature
!= EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE
) {
780 To find FADT in ACPI tables.
782 @return FADT table pointer.
784 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*
789 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
791 Fadt
= FindAcpiFadtTableByAcpiGuid (&gEfiAcpi20TableGuid
);
796 return FindAcpiFadtTableByAcpiGuid (&gEfiAcpi10TableGuid
);
800 To get system port address of the SMI Command Port in FADT table.
808 EFI_ACPI_2_0_FIXED_ACPI_DESCRIPTION_TABLE
*Fadt
;
810 Fadt
= FindAcpiFadtTable ();
811 ASSERT (Fadt
!= NULL
);
813 mSmiCommandPort
= Fadt
->SmiCmd
;
814 DEBUG ((EFI_D_INFO
, "mSmiCommandPort = %x\n", mSmiCommandPort
));
818 Updates page table to make some memory ranges (like system memory) absent
819 and make some memory ranges (like MMIO) present and execute disable. It also
820 update 2MB-page to 4KB-page for some memory ranges.
829 // The flag indicates SMM profile starts to work.
831 mSmmProfileStart
= TRUE
;
835 Initialize SMM profile in SmmReadyToLock protocol callback function.
837 @param Protocol Points to the protocol's unique identifier.
838 @param Interface Points to the interface instance.
839 @param Handle The handle on which the interface was installed.
841 @retval EFI_SUCCESS SmmReadyToLock protocol callback runs successfully.
845 InitSmmProfileCallBack (
846 IN CONST EFI_GUID
*Protocol
,
852 // Save to variable so that SMM profile data can be found.
857 EFI_VARIABLE_BOOTSERVICE_ACCESS
| EFI_VARIABLE_RUNTIME_ACCESS
,
858 sizeof(mSmmProfileBase
),
863 // Get Software SMI from FADT
865 GetSmiCommandPort ();
868 // Initialize protected memory range for patching page table later.
870 InitProtectedMemRange ();
876 Initialize SMM profile data structures.
880 InitSmmProfileInternal (
885 EFI_PHYSICAL_ADDRESS Base
;
888 UINTN MsrDsAreaSizePerCpu
;
891 mPFEntryCount
= (UINTN
*)AllocateZeroPool (sizeof (UINTN
) * mMaxNumberOfCpus
);
892 ASSERT (mPFEntryCount
!= NULL
);
893 mLastPFEntryValue
= (UINT64 (*)[MAX_PF_ENTRY_COUNT
])AllocateZeroPool (
894 sizeof (mLastPFEntryValue
[0]) * mMaxNumberOfCpus
);
895 ASSERT (mLastPFEntryValue
!= NULL
);
896 mLastPFEntryPointer
= (UINT64
*(*)[MAX_PF_ENTRY_COUNT
])AllocateZeroPool (
897 sizeof (mLastPFEntryPointer
[0]) * mMaxNumberOfCpus
);
898 ASSERT (mLastPFEntryPointer
!= NULL
);
901 // Allocate memory for SmmProfile below 4GB.
904 mSmmProfileSize
= PcdGet32 (PcdCpuSmmProfileSize
);
905 ASSERT ((mSmmProfileSize
& 0xFFF) == 0);
908 TotalSize
= mSmmProfileSize
+ mMsrDsAreaSize
;
910 TotalSize
= mSmmProfileSize
;
914 Status
= gBS
->AllocatePages (
916 EfiReservedMemoryType
,
917 EFI_SIZE_TO_PAGES (TotalSize
),
920 ASSERT_EFI_ERROR (Status
);
921 ZeroMem ((VOID
*)(UINTN
)Base
, TotalSize
);
922 mSmmProfileBase
= (SMM_PROFILE_HEADER
*)(UINTN
)Base
;
925 // Initialize SMM profile data header.
927 mSmmProfileBase
->HeaderSize
= sizeof (SMM_PROFILE_HEADER
);
928 mSmmProfileBase
->MaxDataEntries
= (UINT64
)((mSmmProfileSize
- sizeof(SMM_PROFILE_HEADER
)) / sizeof (SMM_PROFILE_ENTRY
));
929 mSmmProfileBase
->MaxDataSize
= MultU64x64 (mSmmProfileBase
->MaxDataEntries
, sizeof(SMM_PROFILE_ENTRY
));
930 mSmmProfileBase
->CurDataEntries
= 0;
931 mSmmProfileBase
->CurDataSize
= 0;
932 mSmmProfileBase
->TsegStart
= mCpuHotPlugData
.SmrrBase
;
933 mSmmProfileBase
->TsegSize
= mCpuHotPlugData
.SmrrSize
;
934 mSmmProfileBase
->NumSmis
= 0;
935 mSmmProfileBase
->NumCpus
= gSmmCpuPrivate
->SmmCoreEntryContext
.NumberOfCpus
;
938 mMsrDsArea
= (MSR_DS_AREA_STRUCT
**)AllocateZeroPool (sizeof (MSR_DS_AREA_STRUCT
*) * mMaxNumberOfCpus
);
939 ASSERT (mMsrDsArea
!= NULL
);
940 mMsrBTSRecord
= (BRANCH_TRACE_RECORD
**)AllocateZeroPool (sizeof (BRANCH_TRACE_RECORD
*) * mMaxNumberOfCpus
);
941 ASSERT (mMsrBTSRecord
!= NULL
);
942 mMsrPEBSRecord
= (PEBS_RECORD
**)AllocateZeroPool (sizeof (PEBS_RECORD
*) * mMaxNumberOfCpus
);
943 ASSERT (mMsrPEBSRecord
!= NULL
);
945 mMsrDsAreaBase
= (MSR_DS_AREA_STRUCT
*)((UINTN
)Base
+ mSmmProfileSize
);
946 MsrDsAreaSizePerCpu
= mMsrDsAreaSize
/ mMaxNumberOfCpus
;
947 mBTSRecordNumber
= (MsrDsAreaSizePerCpu
- sizeof(PEBS_RECORD
) * PEBS_RECORD_NUMBER
- sizeof(MSR_DS_AREA_STRUCT
)) / sizeof(BRANCH_TRACE_RECORD
);
948 for (Index
= 0; Index
< mMaxNumberOfCpus
; Index
++) {
949 mMsrDsArea
[Index
] = (MSR_DS_AREA_STRUCT
*)((UINTN
)mMsrDsAreaBase
+ MsrDsAreaSizePerCpu
* Index
);
950 mMsrBTSRecord
[Index
] = (BRANCH_TRACE_RECORD
*)((UINTN
)mMsrDsArea
[Index
] + sizeof(MSR_DS_AREA_STRUCT
));
951 mMsrPEBSRecord
[Index
] = (PEBS_RECORD
*)((UINTN
)mMsrDsArea
[Index
] + MsrDsAreaSizePerCpu
- sizeof(PEBS_RECORD
) * PEBS_RECORD_NUMBER
);
953 mMsrDsArea
[Index
]->BTSBufferBase
= (UINTN
)mMsrBTSRecord
[Index
];
954 mMsrDsArea
[Index
]->BTSIndex
= mMsrDsArea
[Index
]->BTSBufferBase
;
955 mMsrDsArea
[Index
]->BTSAbsoluteMaximum
= mMsrDsArea
[Index
]->BTSBufferBase
+ mBTSRecordNumber
* sizeof(BRANCH_TRACE_RECORD
) + 1;
956 mMsrDsArea
[Index
]->BTSInterruptThreshold
= mMsrDsArea
[Index
]->BTSAbsoluteMaximum
+ 1;
958 mMsrDsArea
[Index
]->PEBSBufferBase
= (UINTN
)mMsrPEBSRecord
[Index
];
959 mMsrDsArea
[Index
]->PEBSIndex
= mMsrDsArea
[Index
]->PEBSBufferBase
;
960 mMsrDsArea
[Index
]->PEBSAbsoluteMaximum
= mMsrDsArea
[Index
]->PEBSBufferBase
+ PEBS_RECORD_NUMBER
* sizeof(PEBS_RECORD
) + 1;
961 mMsrDsArea
[Index
]->PEBSInterruptThreshold
= mMsrDsArea
[Index
]->PEBSAbsoluteMaximum
+ 1;
965 mProtectionMemRange
= mProtectionMemRangeTemplate
;
966 mProtectionMemRangeCount
= sizeof (mProtectionMemRangeTemplate
) / sizeof (MEMORY_PROTECTION_RANGE
);
969 // Update TSeg entry.
971 mProtectionMemRange
[0].Range
.Base
= mCpuHotPlugData
.SmrrBase
;
972 mProtectionMemRange
[0].Range
.Top
= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
;
975 // Update SMM profile entry.
977 mProtectionMemRange
[1].Range
.Base
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)mSmmProfileBase
;
978 mProtectionMemRange
[1].Range
.Top
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)mSmmProfileBase
+ TotalSize
;
981 // Allocate memory reserved for creating 4KB pages.
983 InitPagesForPFHandler ();
986 // Start SMM profile when SmmReadyToLock protocol is installed.
988 Status
= gSmst
->SmmRegisterProtocolNotify (
989 &gEfiSmmReadyToLockProtocolGuid
,
990 InitSmmProfileCallBack
,
993 ASSERT_EFI_ERROR (Status
);
999 Check if XD feature is supported by a processor.
1003 CheckFeatureSupported (
1009 MSR_IA32_MISC_ENABLE_REGISTER MiscEnableMsr
;
1012 AsmCpuid (CPUID_EXTENDED_FUNCTION
, &RegEax
, NULL
, NULL
, NULL
);
1013 if (RegEax
<= CPUID_EXTENDED_FUNCTION
) {
1015 // Extended CPUID functions are not supported on this processor.
1017 mXdSupported
= FALSE
;
1018 PatchInstructionX86 (gPatchXdSupported
, mXdSupported
, 1);
1021 AsmCpuid (CPUID_EXTENDED_CPU_SIG
, NULL
, NULL
, NULL
, &RegEdx
);
1022 if ((RegEdx
& CPUID1_EDX_XD_SUPPORT
) == 0) {
1024 // Execute Disable Bit feature is not supported on this processor.
1026 mXdSupported
= FALSE
;
1027 PatchInstructionX86 (gPatchXdSupported
, mXdSupported
, 1);
1031 if (mBtsSupported
) {
1032 AsmCpuid (CPUID_VERSION_INFO
, NULL
, NULL
, NULL
, &RegEdx
);
1033 if ((RegEdx
& CPUID1_EDX_BTS_AVAILABLE
) != 0) {
1035 // Per IA32 manuals:
1036 // When CPUID.1:EDX[21] is set, the following BTS facilities are available:
1037 // 1. The BTS_UNAVAILABLE flag in the IA32_MISC_ENABLE MSR indicates the
1038 // availability of the BTS facilities, including the ability to set the BTS and
1039 // BTINT bits in the MSR_DEBUGCTLA MSR.
1040 // 2. The IA32_DS_AREA MSR can be programmed to point to the DS save area.
1042 MiscEnableMsr
.Uint64
= AsmReadMsr64 (MSR_IA32_MISC_ENABLE
);
1043 if (MiscEnableMsr
.Bits
.BTS
== 1) {
1045 // BTS facilities is not supported if MSR_IA32_MISC_ENABLE.BTS bit is set.
1047 mBtsSupported
= FALSE
;
1058 ActivateSingleStepDB (
1064 Dr6
= AsmReadDr6 ();
1065 if ((Dr6
& DR6_SINGLE_STEP
) != 0) {
1068 Dr6
|= DR6_SINGLE_STEP
;
1083 DebugCtl
= AsmReadMsr64 (MSR_DEBUG_CTL
);
1084 if ((DebugCtl
& MSR_DEBUG_CTL_LBR
) != 0) {
1087 DebugCtl
|= MSR_DEBUG_CTL_LBR
;
1088 AsmWriteMsr64 (MSR_DEBUG_CTL
, DebugCtl
);
1092 Enable branch trace store.
1094 @param CpuIndex The index of the processor.
1104 DebugCtl
= AsmReadMsr64 (MSR_DEBUG_CTL
);
1105 if ((DebugCtl
& MSR_DEBUG_CTL_BTS
) != 0) {
1109 AsmWriteMsr64 (MSR_DS_AREA
, (UINT64
)(UINTN
)mMsrDsArea
[CpuIndex
]);
1110 DebugCtl
|= (UINT64
)(MSR_DEBUG_CTL_BTS
| MSR_DEBUG_CTL_TR
);
1111 DebugCtl
&= ~((UINT64
)MSR_DEBUG_CTL_BTINT
);
1112 AsmWriteMsr64 (MSR_DEBUG_CTL
, DebugCtl
);
1116 Increase SMI number in each SMI entry.
1120 SmmProfileRecordSmiNum (
1124 if (mSmmProfileStart
) {
1125 mSmmProfileBase
->NumSmis
++;
1130 Initialize processor environment for SMM profile.
1132 @param CpuIndex The index of the processor.
1136 ActivateSmmProfile (
1141 // Enable Single Step DB#
1143 ActivateSingleStepDB ();
1145 if (mBtsSupported
) {
1147 // We can not get useful information from LER, so we have to use BTS.
1154 ActivateBTS (CpuIndex
);
1159 Initialize SMM profile in SMM CPU entry point.
1161 @param[in] Cr3 The base address of the page tables to use in SMM.
1172 mSmmProfileCr3
= Cr3
;
1175 // Skip SMM profile initialization if feature is disabled
1177 if (!FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
1182 // Initialize SmmProfile here
1184 InitSmmProfileInternal ();
1187 // Initialize profile IDT.
1193 Update page table to map the memory correctly in order to make the instruction
1194 which caused page fault execute successfully. And it also save the original page
1195 table to be restored in single-step exception.
1197 @param PageTable PageTable Address.
1198 @param PFAddress The memory address which caused page fault exception.
1199 @param CpuIndex The index of the processor.
1200 @param ErrorCode The Error code of exception.
1204 RestorePageTableBelow4G (
1217 if (sizeof(UINT64
) == sizeof(UINTN
)) {
1218 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 39, 47);
1219 ASSERT (PageTable
[PTIndex
] != 0);
1220 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1226 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 30, 38);
1227 ASSERT (PageTable
[PTIndex
] != 0);
1228 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1233 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 21, 29);
1234 if ((PageTable
[PTIndex
] & IA32_PG_PS
) != 0) {
1240 // Record old entries with non-present status
1241 // Old entries include the memory which instruction is at and the memory which instruction access.
1244 ASSERT (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
);
1245 if (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
) {
1246 PFIndex
= mPFEntryCount
[CpuIndex
];
1247 mLastPFEntryValue
[CpuIndex
][PFIndex
] = PageTable
[PTIndex
];
1248 mLastPFEntryPointer
[CpuIndex
][PFIndex
] = &PageTable
[PTIndex
];
1249 mPFEntryCount
[CpuIndex
]++;
1255 PageTable
[PTIndex
] = (PFAddress
& ~((1ull << 21) - 1));
1256 PageTable
[PTIndex
] |= (UINT64
)IA32_PG_PS
;
1257 PageTable
[PTIndex
] |= (UINT64
)PAGE_ATTRIBUTE_BITS
;
1258 if ((ErrorCode
& IA32_PF_EC_ID
) != 0) {
1259 PageTable
[PTIndex
] &= ~IA32_PG_NX
;
1265 ASSERT (PageTable
[PTIndex
] != 0);
1266 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & PHYSICAL_ADDRESS_MASK
);
1271 PTIndex
= (UINTN
)BitFieldRead64 (PFAddress
, 12, 20);
1274 // Record old entries with non-present status
1275 // Old entries include the memory which instruction is at and the memory which instruction access.
1278 ASSERT (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
);
1279 if (mPFEntryCount
[CpuIndex
] < MAX_PF_ENTRY_COUNT
) {
1280 PFIndex
= mPFEntryCount
[CpuIndex
];
1281 mLastPFEntryValue
[CpuIndex
][PFIndex
] = PageTable
[PTIndex
];
1282 mLastPFEntryPointer
[CpuIndex
][PFIndex
] = &PageTable
[PTIndex
];
1283 mPFEntryCount
[CpuIndex
]++;
1289 PageTable
[PTIndex
] = (PFAddress
& ~((1ull << 12) - 1));
1290 PageTable
[PTIndex
] |= (UINT64
)PAGE_ATTRIBUTE_BITS
;
1291 if ((ErrorCode
& IA32_PF_EC_ID
) != 0) {
1292 PageTable
[PTIndex
] &= ~IA32_PG_NX
;
1298 The Page fault handler to save SMM profile data.
1300 @param Rip The RIP when exception happens.
1301 @param ErrorCode The Error code of exception.
1305 SmmProfilePFHandler (
1312 UINT64 RestoreAddress
;
1313 UINTN RestorePageNumber
;
1316 UINT64 InstructionAddress
;
1317 UINTN MaxEntryNumber
;
1318 UINTN CurrentEntryNumber
;
1319 BOOLEAN IsValidPFAddress
;
1320 SMM_PROFILE_ENTRY
*SmmProfileEntry
;
1324 EFI_SMM_SAVE_STATE_IO_INFO IoInfo
;
1326 if (!mSmmProfileStart
) {
1328 // If SMM profile does not start, call original page fault handler.
1330 SmiDefaultPFHandler ();
1334 if (mBtsSupported
) {
1338 IsValidPFAddress
= FALSE
;
1339 PageTable
= (UINT64
*)AsmReadCr3 ();
1340 PFAddress
= AsmReadCr2 ();
1341 CpuIndex
= GetCpuIndex ();
1344 // Memory operation cross pages, like "rep mov" instruction, will cause
1345 // infinite loop between this and Debug Trap handler. We have to make sure
1346 // that current page and the page followed are both in PRESENT state.
1348 RestorePageNumber
= 2;
1349 RestoreAddress
= PFAddress
;
1350 while (RestorePageNumber
> 0) {
1351 if (RestoreAddress
<= 0xFFFFFFFF) {
1352 RestorePageTableBelow4G (PageTable
, RestoreAddress
, CpuIndex
, ErrorCode
);
1354 RestorePageTableAbove4G (PageTable
, RestoreAddress
, CpuIndex
, ErrorCode
, &IsValidPFAddress
);
1356 RestoreAddress
+= EFI_PAGE_SIZE
;
1357 RestorePageNumber
--;
1360 if (!IsValidPFAddress
) {
1361 InstructionAddress
= Rip
;
1362 if ((ErrorCode
& IA32_PF_EC_ID
) != 0 && (mBtsSupported
)) {
1364 // If it is instruction fetch failure, get the correct IP from BTS.
1366 InstructionAddress
= GetSourceFromDestinationOnBts (CpuIndex
, Rip
);
1367 if (InstructionAddress
== 0) {
1369 // It indicates the instruction which caused page fault is not a jump instruction,
1370 // set instruction address same as the page fault address.
1372 InstructionAddress
= PFAddress
;
1377 // Indicate it is not software SMI
1379 SmiCommand
= 0xFFFFFFFFFFFFFFFFULL
;
1380 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
1381 Status
= SmmReadSaveState(&mSmmCpu
, sizeof(IoInfo
), EFI_SMM_SAVE_STATE_REGISTER_IO
, Index
, &IoInfo
);
1382 if (EFI_ERROR (Status
)) {
1385 if (IoInfo
.IoPort
== mSmiCommandPort
) {
1387 // A software SMI triggered by SMI command port has been found, get SmiCommand from SMI command port.
1389 SoftSmiValue
= IoRead8 (mSmiCommandPort
);
1390 SmiCommand
= (UINT64
)SoftSmiValue
;
1395 SmmProfileEntry
= (SMM_PROFILE_ENTRY
*)(UINTN
)(mSmmProfileBase
+ 1);
1397 // Check if there is already a same entry in profile data.
1399 for (Index
= 0; Index
< (UINTN
) mSmmProfileBase
->CurDataEntries
; Index
++) {
1400 if ((SmmProfileEntry
[Index
].ErrorCode
== (UINT64
)ErrorCode
) &&
1401 (SmmProfileEntry
[Index
].Address
== PFAddress
) &&
1402 (SmmProfileEntry
[Index
].CpuNum
== (UINT64
)CpuIndex
) &&
1403 (SmmProfileEntry
[Index
].Instruction
== InstructionAddress
) &&
1404 (SmmProfileEntry
[Index
].SmiCmd
== SmiCommand
)) {
1406 // Same record exist, need not save again.
1411 if (Index
== mSmmProfileBase
->CurDataEntries
) {
1412 CurrentEntryNumber
= (UINTN
) mSmmProfileBase
->CurDataEntries
;
1413 MaxEntryNumber
= (UINTN
) mSmmProfileBase
->MaxDataEntries
;
1414 if (FeaturePcdGet (PcdCpuSmmProfileRingBuffer
)) {
1415 CurrentEntryNumber
= CurrentEntryNumber
% MaxEntryNumber
;
1417 if (CurrentEntryNumber
< MaxEntryNumber
) {
1419 // Log the new entry
1421 SmmProfileEntry
[CurrentEntryNumber
].SmiNum
= mSmmProfileBase
->NumSmis
;
1422 SmmProfileEntry
[CurrentEntryNumber
].ErrorCode
= (UINT64
)ErrorCode
;
1423 SmmProfileEntry
[CurrentEntryNumber
].ApicId
= (UINT64
)GetApicId ();
1424 SmmProfileEntry
[CurrentEntryNumber
].CpuNum
= (UINT64
)CpuIndex
;
1425 SmmProfileEntry
[CurrentEntryNumber
].Address
= PFAddress
;
1426 SmmProfileEntry
[CurrentEntryNumber
].Instruction
= InstructionAddress
;
1427 SmmProfileEntry
[CurrentEntryNumber
].SmiCmd
= SmiCommand
;
1429 // Update current entry index and data size in the header.
1431 mSmmProfileBase
->CurDataEntries
++;
1432 mSmmProfileBase
->CurDataSize
= MultU64x64 (mSmmProfileBase
->CurDataEntries
, sizeof (SMM_PROFILE_ENTRY
));
1441 if (mBtsSupported
) {
1447 Replace INT1 exception handler to restore page table to absent/execute-disable state
1448 in order to trigger page fault again to save SMM profile data..
1458 Status
= SmmRegisterExceptionHandler (&mSmmCpuService
, EXCEPT_IA32_DEBUG
, DebugExceptionHandler
);
1459 ASSERT_EFI_ERROR (Status
);