2 Page Fault (#PF) handler for X64 processors
4 Copyright (c) 2009 - 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"
19 #define PAGE_TABLE_PAGES 8
20 #define ACC_MAX_BIT BIT3
22 LIST_ENTRY mPagePool
= INITIALIZE_LIST_HEAD_VARIABLE (mPagePool
);
23 BOOLEAN m1GPageTableSupport
= FALSE
;
24 BOOLEAN mCpuSmmStaticPageTable
;
27 Check if 1-GByte pages is supported by processor or not.
29 @retval TRUE 1-GByte pages is supported.
30 @retval FALSE 1-GByte pages is not supported.
41 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
42 if (RegEax
>= 0x80000001) {
43 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
44 if ((RegEdx
& BIT26
) != 0) {
52 Set sub-entries number in entry.
54 @param[in, out] Entry Pointer to entry
55 @param[in] SubEntryNum Sub-entries number based on 0:
56 0 means there is 1 sub-entry under this entry
57 0x1ff means there is 512 sub-entries under this entry
67 // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
69 *Entry
= BitFieldWrite64 (*Entry
, 52, 60, SubEntryNum
);
73 Return sub-entries number in entry.
75 @param[in] Entry Pointer to entry
77 @return Sub-entries number based on 0:
78 0 means there is 1 sub-entry under this entry
79 0x1ff means there is 512 sub-entries under this entry
87 // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
89 return BitFieldRead64 (*Entry
, 52, 60);
93 Calculate the maximum support address.
95 @return the maximum support address.
98 CalculateMaximumSupportAddress (
103 UINT8 PhysicalAddressBits
;
107 // Get physical address bits supported.
109 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
111 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
113 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
114 if (RegEax
>= 0x80000008) {
115 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
116 PhysicalAddressBits
= (UINT8
) RegEax
;
118 PhysicalAddressBits
= 36;
123 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
125 ASSERT (PhysicalAddressBits
<= 52);
126 if (PhysicalAddressBits
> 48) {
127 PhysicalAddressBits
= 48;
129 return PhysicalAddressBits
;
133 Set static page table.
135 @param[in] PageTable Address of page table.
143 UINTN NumberOfPml4EntriesNeeded
;
144 UINTN NumberOfPdpEntriesNeeded
;
145 UINTN IndexOfPml4Entries
;
146 UINTN IndexOfPdpEntries
;
147 UINTN IndexOfPageDirectoryEntries
;
148 UINT64
*PageMapLevel4Entry
;
150 UINT64
*PageDirectoryPointerEntry
;
151 UINT64
*PageDirectory1GEntry
;
152 UINT64
*PageDirectoryEntry
;
154 if (mPhysicalAddressBits
<= 39 ) {
155 NumberOfPml4EntriesNeeded
= 1;
156 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (mPhysicalAddressBits
- 30));
158 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (mPhysicalAddressBits
- 39));
159 NumberOfPdpEntriesNeeded
= 512;
163 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
165 PageMap
= (VOID
*) PageTable
;
167 PageMapLevel4Entry
= PageMap
;
169 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
171 // Each PML4 entry points to a page of Page Directory Pointer entries.
173 PageDirectoryPointerEntry
= (UINT64
*) ((*PageMapLevel4Entry
) & ~mAddressEncMask
& gPhyMask
);
174 if (PageDirectoryPointerEntry
== NULL
) {
175 PageDirectoryPointerEntry
= AllocatePageTableMemory (1);
176 ASSERT(PageDirectoryPointerEntry
!= NULL
);
177 ZeroMem (PageDirectoryPointerEntry
, EFI_PAGES_TO_SIZE(1));
179 *PageMapLevel4Entry
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
182 if (m1GPageTableSupport
) {
183 PageDirectory1GEntry
= PageDirectoryPointerEntry
;
184 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
185 if (IndexOfPml4Entries
== 0 && IndexOfPageDirectoryEntries
< 4) {
187 // Skip the < 4G entries
192 // Fill in the Page Directory entries
194 *PageDirectory1GEntry
= PageAddress
| mAddressEncMask
| IA32_PG_PS
| PAGE_ATTRIBUTE_BITS
;
197 PageAddress
= BASE_4GB
;
198 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
199 if (IndexOfPml4Entries
== 0 && IndexOfPdpEntries
< 4) {
201 // Skip the < 4G entries
206 // Each Directory Pointer entries points to a page of Page Directory entires.
207 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
209 PageDirectoryEntry
= (UINT64
*) ((*PageDirectoryPointerEntry
) & ~mAddressEncMask
& gPhyMask
);
210 if (PageDirectoryEntry
== NULL
) {
211 PageDirectoryEntry
= AllocatePageTableMemory (1);
212 ASSERT(PageDirectoryEntry
!= NULL
);
213 ZeroMem (PageDirectoryEntry
, EFI_PAGES_TO_SIZE(1));
216 // Fill in a Page Directory Pointer Entries
218 *PageDirectoryPointerEntry
= (UINT64
)(UINTN
)PageDirectoryEntry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
221 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
223 // Fill in the Page Directory entries
225 *PageDirectoryEntry
= PageAddress
| mAddressEncMask
| IA32_PG_PS
| PAGE_ATTRIBUTE_BITS
;
233 Create PageTable for SMM use.
235 @return The address of PML4 (to set CR3).
243 EFI_PHYSICAL_ADDRESS Pages
;
245 LIST_ENTRY
*FreePage
;
247 UINTN PageFaultHandlerHookAddress
;
248 IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
;
252 // Initialize spin lock
254 InitializeSpinLock (mPFLock
);
256 mCpuSmmStaticPageTable
= PcdGetBool (PcdCpuSmmStaticPageTable
);
257 m1GPageTableSupport
= Is1GPageSupport ();
258 DEBUG ((DEBUG_INFO
, "1GPageTableSupport - 0x%x\n", m1GPageTableSupport
));
259 DEBUG ((DEBUG_INFO
, "PcdCpuSmmStaticPageTable - 0x%x\n", mCpuSmmStaticPageTable
));
261 mPhysicalAddressBits
= CalculateMaximumSupportAddress ();
262 DEBUG ((DEBUG_INFO
, "PhysicalAddressBits - 0x%x\n", mPhysicalAddressBits
));
264 // Generate PAE page table for the first 4GB memory space
266 Pages
= Gen4GPageTable (FALSE
);
269 // Set IA32_PG_PMNT bit to mask this entry
271 PTEntry
= (UINT64
*)(UINTN
)Pages
;
272 for (Index
= 0; Index
< 4; Index
++) {
273 PTEntry
[Index
] |= IA32_PG_PMNT
;
277 // Fill Page-Table-Level4 (PML4) entry
279 PTEntry
= (UINT64
*)AllocatePageTableMemory (1);
280 ASSERT (PTEntry
!= NULL
);
281 *PTEntry
= Pages
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
282 ZeroMem (PTEntry
+ 1, EFI_PAGE_SIZE
- sizeof (*PTEntry
));
285 // Set sub-entries number
287 SetSubEntriesNum (PTEntry
, 3);
289 if (mCpuSmmStaticPageTable
) {
290 SetStaticPageTable ((UINTN
)PTEntry
);
293 // Add pages to page pool
295 FreePage
= (LIST_ENTRY
*)AllocatePageTableMemory (PAGE_TABLE_PAGES
);
296 ASSERT (FreePage
!= NULL
);
297 for (Index
= 0; Index
< PAGE_TABLE_PAGES
; Index
++) {
298 InsertTailList (&mPagePool
, FreePage
);
299 FreePage
+= EFI_PAGE_SIZE
/ sizeof (*FreePage
);
303 if (FeaturePcdGet (PcdCpuSmmProfileEnable
) ||
304 HEAP_GUARD_NONSTOP_MODE
||
305 NULL_DETECTION_NONSTOP_MODE
) {
307 // Set own Page Fault entry instead of the default one, because SMM Profile
308 // feature depends on IRET instruction to do Single Step
310 PageFaultHandlerHookAddress
= (UINTN
)PageFaultIdtHandlerSmmProfile
;
311 IdtEntry
= (IA32_IDT_GATE_DESCRIPTOR
*) gcSmiIdtr
.Base
;
312 IdtEntry
+= EXCEPT_IA32_PAGE_FAULT
;
313 IdtEntry
->Bits
.OffsetLow
= (UINT16
)PageFaultHandlerHookAddress
;
314 IdtEntry
->Bits
.Reserved_0
= 0;
315 IdtEntry
->Bits
.GateType
= IA32_IDT_GATE_TYPE_INTERRUPT_32
;
316 IdtEntry
->Bits
.OffsetHigh
= (UINT16
)(PageFaultHandlerHookAddress
>> 16);
317 IdtEntry
->Bits
.OffsetUpper
= (UINT32
)(PageFaultHandlerHookAddress
>> 32);
318 IdtEntry
->Bits
.Reserved_1
= 0;
321 // Register Smm Page Fault Handler
323 Status
= SmmRegisterExceptionHandler (&mSmmCpuService
, EXCEPT_IA32_PAGE_FAULT
, SmiPFHandler
);
324 ASSERT_EFI_ERROR (Status
);
328 // Additional SMM IDT initialization for SMM stack guard
330 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
331 InitializeIDTSmmStackGuard ();
335 // Return the address of PML4 (to set CR3)
337 return (UINT32
)(UINTN
)PTEntry
;
341 Set access record in entry.
343 @param[in, out] Entry Pointer to entry
344 @param[in] Acc Access record value
349 IN OUT UINT64
*Entry
,
354 // Access record is saved in BIT9 to BIT11 (reserved field) in Entry
356 *Entry
= BitFieldWrite64 (*Entry
, 9, 11, Acc
);
360 Return access record in entry.
362 @param[in] Entry Pointer to entry
364 @return Access record value.
373 // Access record is saved in BIT9 to BIT11 (reserved field) in Entry
375 return BitFieldRead64 (*Entry
, 9, 11);
379 Return and update the access record in entry.
381 @param[in, out] Entry Pointer to entry
383 @return Access record value.
393 Acc
= GetAccNum (Entry
);
394 if ((*Entry
& IA32_PG_A
) != 0) {
396 // If this entry has been accessed, clear access flag in Entry and update access record
397 // to the initial value 7, adding ACC_MAX_BIT is to make it larger than others
399 *Entry
&= ~(UINT64
)(UINTN
)IA32_PG_A
;
400 SetAccNum (Entry
, 0x7);
401 return (0x7 + ACC_MAX_BIT
);
405 // If the access record is not the smallest value 0, minus 1 and update the access record field
407 SetAccNum (Entry
, Acc
- 1);
414 Reclaim free pages for PageFault handler.
416 Search the whole entries tree to find the leaf entry that has the smallest
417 access record value. Insert the page pointed by this leaf entry into the
418 page pool. And check its upper entries if need to be inserted into the page
438 UINT64 SubEntriesNum
;
441 UINT64
*ReleasePageAddress
;
451 ReleasePageAddress
= 0;
454 // First, find the leaf entry has the smallest access record value
456 Pml4
= (UINT64
*)(UINTN
)(AsmReadCr3 () & gPhyMask
);
457 for (Pml4Index
= 0; Pml4Index
< EFI_PAGE_SIZE
/ sizeof (*Pml4
); Pml4Index
++) {
458 if ((Pml4
[Pml4Index
] & IA32_PG_P
) == 0 || (Pml4
[Pml4Index
] & IA32_PG_PMNT
) != 0) {
460 // If the PML4 entry is not present or is masked, skip it
464 Pdpt
= (UINT64
*)(UINTN
)(Pml4
[Pml4Index
] & ~mAddressEncMask
& gPhyMask
);
466 for (PdptIndex
= 0; PdptIndex
< EFI_PAGE_SIZE
/ sizeof (*Pdpt
); PdptIndex
++) {
467 if ((Pdpt
[PdptIndex
] & IA32_PG_P
) == 0 || (Pdpt
[PdptIndex
] & IA32_PG_PMNT
) != 0) {
469 // If the PDPT entry is not present or is masked, skip it
471 if ((Pdpt
[PdptIndex
] & IA32_PG_PMNT
) != 0) {
473 // If the PDPT entry is masked, we will ignore checking the PML4 entry
479 if ((Pdpt
[PdptIndex
] & IA32_PG_PS
) == 0) {
481 // It's not 1-GByte pages entry, it should be a PDPT entry,
482 // we will not check PML4 entry more
485 Pdt
= (UINT64
*)(UINTN
)(Pdpt
[PdptIndex
] & ~mAddressEncMask
& gPhyMask
);
487 for (PdtIndex
= 0; PdtIndex
< EFI_PAGE_SIZE
/ sizeof(*Pdt
); PdtIndex
++) {
488 if ((Pdt
[PdtIndex
] & IA32_PG_P
) == 0 || (Pdt
[PdtIndex
] & IA32_PG_PMNT
) != 0) {
490 // If the PD entry is not present or is masked, skip it
492 if ((Pdt
[PdtIndex
] & IA32_PG_PMNT
) != 0) {
494 // If the PD entry is masked, we will not PDPT entry more
500 if ((Pdt
[PdtIndex
] & IA32_PG_PS
) == 0) {
502 // It's not 2 MByte page table entry, it should be PD entry
503 // we will find the entry has the smallest access record value
506 Acc
= GetAndUpdateAccNum (Pdt
+ PdtIndex
);
509 // If the PD entry has the smallest access record value,
510 // save the Page address to be released
516 ReleasePageAddress
= Pdt
+ PdtIndex
;
522 // If this PDPT entry has no PDT entries pointer to 4 KByte pages,
523 // it should only has the entries point to 2 MByte Pages
525 Acc
= GetAndUpdateAccNum (Pdpt
+ PdptIndex
);
528 // If the PDPT entry has the smallest access record value,
529 // save the Page address to be released
535 ReleasePageAddress
= Pdpt
+ PdptIndex
;
542 // If PML4 entry has no the PDPT entry pointer to 2 MByte pages,
543 // it should only has the entries point to 1 GByte Pages
545 Acc
= GetAndUpdateAccNum (Pml4
+ Pml4Index
);
548 // If the PML4 entry has the smallest access record value,
549 // save the Page address to be released
555 ReleasePageAddress
= Pml4
+ Pml4Index
;
560 // Make sure one PML4/PDPT/PD entry is selected
562 ASSERT (MinAcc
!= (UINT64
)-1);
565 // Secondly, insert the page pointed by this entry into page pool and clear this entry
567 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(*ReleasePageAddress
& ~mAddressEncMask
& gPhyMask
));
568 *ReleasePageAddress
= 0;
571 // Lastly, check this entry's upper entries if need to be inserted into page pool
575 if (MinPdt
!= (UINTN
)-1) {
577 // If 4 KByte Page Table is released, check the PDPT entry
579 Pdpt
= (UINT64
*)(UINTN
)(Pml4
[MinPml4
] & ~mAddressEncMask
& gPhyMask
);
580 SubEntriesNum
= GetSubEntriesNum(Pdpt
+ MinPdpt
);
581 if (SubEntriesNum
== 0) {
583 // Release the empty Page Directory table if there was no more 4 KByte Page Table entry
584 // clear the Page directory entry
586 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(Pdpt
[MinPdpt
] & ~mAddressEncMask
& gPhyMask
));
589 // Go on checking the PML4 table
595 // Update the sub-entries filed in PDPT entry and exit
597 SetSubEntriesNum (Pdpt
+ MinPdpt
, SubEntriesNum
- 1);
600 if (MinPdpt
!= (UINTN
)-1) {
602 // One 2MB Page Table is released or Page Directory table is released, check the PML4 entry
604 SubEntriesNum
= GetSubEntriesNum (Pml4
+ MinPml4
);
605 if (SubEntriesNum
== 0) {
607 // Release the empty PML4 table if there was no more 1G KByte Page Table entry
608 // clear the Page directory entry
610 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(Pml4
[MinPml4
] & ~mAddressEncMask
& gPhyMask
));
616 // Update the sub-entries filed in PML4 entry and exit
618 SetSubEntriesNum (Pml4
+ MinPml4
, SubEntriesNum
- 1);
622 // PLM4 table has been released before, exit it
629 Allocate free Page for PageFault handler use.
631 @return Page address.
641 if (IsListEmpty (&mPagePool
)) {
643 // If page pool is empty, reclaim the used pages and insert one into page pool
649 // Get one free page and remove it from page pool
651 RetVal
= (UINT64
)(UINTN
)mPagePool
.ForwardLink
;
652 RemoveEntryList (mPagePool
.ForwardLink
);
654 // Clean this page and return
656 ZeroMem ((VOID
*)(UINTN
)RetVal
, EFI_PAGE_SIZE
);
661 Page Fault handler for SMM use.
665 SmiDefaultPFHandler (
676 SMM_PAGE_SIZE_TYPE PageSize
;
683 // Set default SMM page attribute
685 PageSize
= SmmPageSize2M
;
690 Pml4
= (UINT64
*)(AsmReadCr3 () & gPhyMask
);
691 PFAddress
= AsmReadCr2 ();
693 Status
= GetPlatformPageTableAttribute (PFAddress
, &PageSize
, &NumOfPages
, &PageAttribute
);
695 // If platform not support page table attribute, set default SMM page attribute
697 if (Status
!= EFI_SUCCESS
) {
698 PageSize
= SmmPageSize2M
;
702 if (PageSize
>= MaxSmmPageSizeType
) {
703 PageSize
= SmmPageSize2M
;
705 if (NumOfPages
> 512) {
712 // BIT12 to BIT20 is Page Table index
718 // BIT21 to BIT29 is Page Directory index
721 PageAttribute
|= (UINTN
)IA32_PG_PS
;
724 if (!m1GPageTableSupport
) {
725 DEBUG ((DEBUG_ERROR
, "1-GByte pages is not supported!"));
729 // BIT30 to BIT38 is Page Directory Pointer Table index
732 PageAttribute
|= (UINTN
)IA32_PG_PS
;
739 // If execute-disable is enabled, set NX bit
742 PageAttribute
|= IA32_PG_NX
;
745 for (Index
= 0; Index
< NumOfPages
; Index
++) {
748 for (StartBit
= 39; StartBit
> EndBit
; StartBit
-= 9) {
749 PTIndex
= BitFieldRead64 (PFAddress
, StartBit
, StartBit
+ 8);
750 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
752 // If the entry is not present, allocate one page from page pool for it
754 PageTable
[PTIndex
] = AllocPage () | mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
757 // Save the upper entry address
759 UpperEntry
= PageTable
+ PTIndex
;
762 // BIT9 to BIT11 of entry is used to save access record,
763 // initialize value is 7
765 PageTable
[PTIndex
] |= (UINT64
)IA32_PG_A
;
766 SetAccNum (PageTable
+ PTIndex
, 7);
767 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & ~mAddressEncMask
& gPhyMask
);
770 PTIndex
= BitFieldRead64 (PFAddress
, StartBit
, StartBit
+ 8);
771 if ((PageTable
[PTIndex
] & IA32_PG_P
) != 0) {
773 // Check if the entry has already existed, this issue may occur when the different
774 // size page entries created under the same entry
776 DEBUG ((DEBUG_ERROR
, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable
, PTIndex
, PageTable
[PTIndex
]));
777 DEBUG ((DEBUG_ERROR
, "New page table overlapped with old page table!\n"));
781 // Fill the new entry
783 PageTable
[PTIndex
] = ((PFAddress
| mAddressEncMask
) & gPhyMask
& ~((1ull << EndBit
) - 1)) |
784 PageAttribute
| IA32_PG_A
| PAGE_ATTRIBUTE_BITS
;
785 if (UpperEntry
!= NULL
) {
786 SetSubEntriesNum (UpperEntry
, GetSubEntriesNum (UpperEntry
) + 1);
789 // Get the next page address if we need to create more page tables
791 PFAddress
+= (1ull << EndBit
);
796 ThePage Fault handler wrapper for SMM use.
798 @param InterruptType Defines the type of interrupt or exception that
799 occurred on the processor.This parameter is processor architecture specific.
800 @param SystemContext A pointer to the processor context when
801 the interrupt occurred on the processor.
806 IN EFI_EXCEPTION_TYPE InterruptType
,
807 IN EFI_SYSTEM_CONTEXT SystemContext
811 UINTN GuardPageAddress
;
814 ASSERT (InterruptType
== EXCEPT_IA32_PAGE_FAULT
);
816 AcquireSpinLock (mPFLock
);
818 PFAddress
= AsmReadCr2 ();
820 if (mCpuSmmStaticPageTable
&& (PFAddress
>= LShiftU64 (1, (mPhysicalAddressBits
- 1)))) {
821 DumpCpuContext (InterruptType
, SystemContext
);
822 DEBUG ((DEBUG_ERROR
, "Do not support address 0x%lx by processor!\n", PFAddress
));
827 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
828 // or SMM page protection violation.
830 if ((PFAddress
>= mCpuHotPlugData
.SmrrBase
) &&
831 (PFAddress
< (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
))) {
832 DumpCpuContext (InterruptType
, SystemContext
);
833 CpuIndex
= GetCpuIndex ();
834 GuardPageAddress
= (mSmmStackArrayBase
+ EFI_PAGE_SIZE
+ CpuIndex
* mSmmStackSize
);
835 if ((FeaturePcdGet (PcdCpuSmmStackGuard
)) &&
836 (PFAddress
>= GuardPageAddress
) &&
837 (PFAddress
< (GuardPageAddress
+ EFI_PAGE_SIZE
))) {
838 DEBUG ((DEBUG_ERROR
, "SMM stack overflow!\n"));
840 if ((SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_ID
) != 0) {
841 DEBUG ((DEBUG_ERROR
, "SMM exception at execution (0x%lx)\n", PFAddress
));
843 DumpModuleInfoByIp (*(UINTN
*)(UINTN
)SystemContext
.SystemContextX64
->Rsp
);
846 DEBUG ((DEBUG_ERROR
, "SMM exception at access (0x%lx)\n", PFAddress
));
848 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextX64
->Rip
);
852 if (HEAP_GUARD_NONSTOP_MODE
) {
853 GuardPagePFHandler (SystemContext
.SystemContextX64
->ExceptionData
);
861 // If a page fault occurs in non-SMRAM range.
863 if ((PFAddress
< mCpuHotPlugData
.SmrrBase
) ||
864 (PFAddress
>= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
)) {
865 if ((SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_ID
) != 0) {
866 DumpCpuContext (InterruptType
, SystemContext
);
867 DEBUG ((DEBUG_ERROR
, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress
));
869 DumpModuleInfoByIp (*(UINTN
*)(UINTN
)SystemContext
.SystemContextX64
->Rsp
);
875 // If NULL pointer was just accessed
877 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & BIT1
) != 0 &&
878 (PFAddress
< EFI_PAGE_SIZE
)) {
879 DumpCpuContext (InterruptType
, SystemContext
);
880 DEBUG ((DEBUG_ERROR
, "!!! NULL pointer access !!!\n"));
882 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextX64
->Rip
);
885 if (NULL_DETECTION_NONSTOP_MODE
) {
886 GuardPagePFHandler (SystemContext
.SystemContextX64
->ExceptionData
);
893 if (IsSmmCommBufferForbiddenAddress (PFAddress
)) {
894 DumpCpuContext (InterruptType
, SystemContext
);
895 DEBUG ((DEBUG_ERROR
, "Access SMM communication forbidden address (0x%lx)!\n", PFAddress
));
897 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextX64
->Rip
);
903 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
904 SmmProfilePFHandler (
905 SystemContext
.SystemContextX64
->Rip
,
906 SystemContext
.SystemContextX64
->ExceptionData
909 SmiDefaultPFHandler ();
913 ReleaseSpinLock (mPFLock
);
917 This function sets memory attribute for page table.
920 SetPageTableAttributes (
932 BOOLEAN PageTableSplitted
;
936 // - no static page table; or
937 // - SMM heap guard feature enabled; or
938 // BIT2: SMM page guard enabled
939 // BIT3: SMM pool guard enabled
940 // - SMM profile feature enabled
942 if (!mCpuSmmStaticPageTable
||
943 ((PcdGet8 (PcdHeapGuardPropertyMask
) & (BIT3
| BIT2
)) != 0) ||
944 FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
946 // Static paging and heap guard could not be enabled at the same time.
948 ASSERT (!(mCpuSmmStaticPageTable
&&
949 (PcdGet8 (PcdHeapGuardPropertyMask
) & (BIT3
| BIT2
)) != 0));
952 // Static paging and SMM profile could not be enabled at the same time.
954 ASSERT (!(mCpuSmmStaticPageTable
&& FeaturePcdGet (PcdCpuSmmProfileEnable
)));
958 DEBUG ((DEBUG_INFO
, "SetPageTableAttributes\n"));
961 // Disable write protection, because we need mark page table to be write protected.
962 // We need *write* page table memory, to mark itself to be *read only*.
964 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP
);
967 DEBUG ((DEBUG_INFO
, "Start...\n"));
968 PageTableSplitted
= FALSE
;
970 L4PageTable
= (UINT64
*)GetPageTableBase ();
971 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L4PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
972 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
974 for (Index4
= 0; Index4
< SIZE_4KB
/sizeof(UINT64
); Index4
++) {
975 L3PageTable
= (UINT64
*)(UINTN
)(L4PageTable
[Index4
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
976 if (L3PageTable
== NULL
) {
980 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L3PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
981 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
983 for (Index3
= 0; Index3
< SIZE_4KB
/sizeof(UINT64
); Index3
++) {
984 if ((L3PageTable
[Index3
] & IA32_PG_PS
) != 0) {
988 L2PageTable
= (UINT64
*)(UINTN
)(L3PageTable
[Index3
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
989 if (L2PageTable
== NULL
) {
993 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L2PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
994 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
996 for (Index2
= 0; Index2
< SIZE_4KB
/sizeof(UINT64
); Index2
++) {
997 if ((L2PageTable
[Index2
] & IA32_PG_PS
) != 0) {
1001 L1PageTable
= (UINT64
*)(UINTN
)(L2PageTable
[Index2
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
1002 if (L1PageTable
== NULL
) {
1005 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L1PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
1006 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
1010 } while (PageTableSplitted
);
1013 // Enable write protection, after page table updated.
1015 AsmWriteCr0 (AsmReadCr0() | CR0_WP
);