2 Page Fault (#PF) handler for X64 processors
4 Copyright (c) 2009 - 2019, Intel Corporation. All rights reserved.<BR>
5 Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>
7 SPDX-License-Identifier: BSD-2-Clause-Patent
11 #include "PiSmmCpuDxeSmm.h"
13 #define PAGE_TABLE_PAGES 8
14 #define ACC_MAX_BIT BIT3
16 extern UINTN mSmmShadowStackSize
;
18 LIST_ENTRY mPagePool
= INITIALIZE_LIST_HEAD_VARIABLE (mPagePool
);
19 BOOLEAN m1GPageTableSupport
= FALSE
;
20 BOOLEAN mCpuSmmRestrictedMemoryAccess
;
21 BOOLEAN m5LevelPagingNeeded
;
22 X86_ASSEMBLY_PATCH_LABEL gPatch5LevelPagingNeeded
;
43 Check if 1-GByte pages is supported by processor or not.
45 @retval TRUE 1-GByte pages is supported.
46 @retval FALSE 1-GByte pages is not supported.
57 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
58 if (RegEax
>= 0x80000001) {
59 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
60 if ((RegEdx
& BIT26
) != 0) {
68 The routine returns TRUE when CPU supports it (CPUID[7,0].ECX.BIT[16] is set) and
69 the max physical address bits is bigger than 48. Because 4-level paging can support
70 to address physical address up to 2^48 - 1, there is no need to enable 5-level paging
71 with max physical address bits <= 48.
73 @retval TRUE 5-level paging enabling is needed.
74 @retval FALSE 5-level paging enabling is not needed.
77 Is5LevelPagingNeeded (
81 CPUID_VIR_PHY_ADDRESS_SIZE_EAX VirPhyAddressSize
;
82 CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_ECX ExtFeatureEcx
;
83 UINT32 MaxExtendedFunctionId
;
85 AsmCpuid (CPUID_EXTENDED_FUNCTION
, &MaxExtendedFunctionId
, NULL
, NULL
, NULL
);
86 if (MaxExtendedFunctionId
>= CPUID_VIR_PHY_ADDRESS_SIZE
) {
87 AsmCpuid (CPUID_VIR_PHY_ADDRESS_SIZE
, &VirPhyAddressSize
.Uint32
, NULL
, NULL
, NULL
);
89 VirPhyAddressSize
.Bits
.PhysicalAddressBits
= 36;
92 CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS
,
93 CPUID_STRUCTURED_EXTENDED_FEATURE_FLAGS_SUB_LEAF_INFO
,
94 NULL
, NULL
, &ExtFeatureEcx
.Uint32
, NULL
97 DEBUG_INFO
, "PhysicalAddressBits = %d, 5LPageTable = %d.\n",
98 VirPhyAddressSize
.Bits
.PhysicalAddressBits
, ExtFeatureEcx
.Bits
.FiveLevelPage
101 if (VirPhyAddressSize
.Bits
.PhysicalAddressBits
> 4 * 9 + 12) {
102 ASSERT (ExtFeatureEcx
.Bits
.FiveLevelPage
== 1);
110 Get page table base address and the depth of the page table.
112 @param[out] Base Page table base address.
113 @param[out] FiveLevels TRUE means 5 level paging. FALSE means 4 level paging.
118 OUT BOOLEAN
*FiveLevels OPTIONAL
123 if (mInternalCr3
== 0) {
124 *Base
= AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
;
125 if (FiveLevels
!= NULL
) {
126 Cr4
.UintN
= AsmReadCr4 ();
127 *FiveLevels
= (BOOLEAN
)(Cr4
.Bits
.LA57
== 1);
132 *Base
= mInternalCr3
;
133 if (FiveLevels
!= NULL
) {
134 *FiveLevels
= m5LevelPagingNeeded
;
139 Set sub-entries number in entry.
141 @param[in, out] Entry Pointer to entry
142 @param[in] SubEntryNum Sub-entries number based on 0:
143 0 means there is 1 sub-entry under this entry
144 0x1ff means there is 512 sub-entries under this entry
149 IN OUT UINT64
*Entry
,
150 IN UINT64 SubEntryNum
154 // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
156 *Entry
= BitFieldWrite64 (*Entry
, 52, 60, SubEntryNum
);
160 Return sub-entries number in entry.
162 @param[in] Entry Pointer to entry
164 @return Sub-entries number based on 0:
165 0 means there is 1 sub-entry under this entry
166 0x1ff means there is 512 sub-entries under this entry
174 // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
176 return BitFieldRead64 (*Entry
, 52, 60);
180 Calculate the maximum support address.
182 @return the maximum support address.
185 CalculateMaximumSupportAddress (
190 UINT8 PhysicalAddressBits
;
194 // Get physical address bits supported.
196 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
198 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
200 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
201 if (RegEax
>= 0x80000008) {
202 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
203 PhysicalAddressBits
= (UINT8
) RegEax
;
205 PhysicalAddressBits
= 36;
208 return PhysicalAddressBits
;
212 Set static page table.
214 @param[in] PageTable Address of page table.
215 @param[in] PhysicalAddressBits The maximum physical address bits supported.
220 IN UINT8 PhysicalAddressBits
224 UINTN NumberOfPml5EntriesNeeded
;
225 UINTN NumberOfPml4EntriesNeeded
;
226 UINTN NumberOfPdpEntriesNeeded
;
227 UINTN IndexOfPml5Entries
;
228 UINTN IndexOfPml4Entries
;
229 UINTN IndexOfPdpEntries
;
230 UINTN IndexOfPageDirectoryEntries
;
231 UINT64
*PageMapLevel5Entry
;
232 UINT64
*PageMapLevel4Entry
;
234 UINT64
*PageDirectoryPointerEntry
;
235 UINT64
*PageDirectory1GEntry
;
236 UINT64
*PageDirectoryEntry
;
239 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses
240 // when 5-Level Paging is disabled.
242 ASSERT (PhysicalAddressBits
<= 52);
243 if (!m5LevelPagingNeeded
&& PhysicalAddressBits
> 48) {
244 PhysicalAddressBits
= 48;
247 NumberOfPml5EntriesNeeded
= 1;
248 if (PhysicalAddressBits
> 48) {
249 NumberOfPml5EntriesNeeded
= (UINTN
) LShiftU64 (1, PhysicalAddressBits
- 48);
250 PhysicalAddressBits
= 48;
253 NumberOfPml4EntriesNeeded
= 1;
254 if (PhysicalAddressBits
> 39) {
255 NumberOfPml4EntriesNeeded
= (UINTN
) LShiftU64 (1, PhysicalAddressBits
- 39);
256 PhysicalAddressBits
= 39;
259 NumberOfPdpEntriesNeeded
= 1;
260 ASSERT (PhysicalAddressBits
> 30);
261 NumberOfPdpEntriesNeeded
= (UINTN
) LShiftU64 (1, PhysicalAddressBits
- 30);
264 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
266 PageMap
= (VOID
*) PageTable
;
268 PageMapLevel4Entry
= PageMap
;
269 PageMapLevel5Entry
= NULL
;
270 if (m5LevelPagingNeeded
) {
272 // By architecture only one PageMapLevel5 exists - so lets allocate storage for it.
274 PageMapLevel5Entry
= PageMap
;
278 for ( IndexOfPml5Entries
= 0
279 ; IndexOfPml5Entries
< NumberOfPml5EntriesNeeded
280 ; IndexOfPml5Entries
++, PageMapLevel5Entry
++) {
282 // Each PML5 entry points to a page of PML4 entires.
283 // So lets allocate space for them and fill them in in the IndexOfPml4Entries loop.
284 // When 5-Level Paging is disabled, below allocation happens only once.
286 if (m5LevelPagingNeeded
) {
287 PageMapLevel4Entry
= (UINT64
*) ((*PageMapLevel5Entry
) & ~mAddressEncMask
& gPhyMask
);
288 if (PageMapLevel4Entry
== NULL
) {
289 PageMapLevel4Entry
= AllocatePageTableMemory (1);
290 ASSERT(PageMapLevel4Entry
!= NULL
);
291 ZeroMem (PageMapLevel4Entry
, EFI_PAGES_TO_SIZE(1));
293 *PageMapLevel5Entry
= (UINT64
)(UINTN
)PageMapLevel4Entry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
297 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< (NumberOfPml5EntriesNeeded
== 1 ? NumberOfPml4EntriesNeeded
: 512); IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
299 // Each PML4 entry points to a page of Page Directory Pointer entries.
301 PageDirectoryPointerEntry
= (UINT64
*) ((*PageMapLevel4Entry
) & ~mAddressEncMask
& gPhyMask
);
302 if (PageDirectoryPointerEntry
== NULL
) {
303 PageDirectoryPointerEntry
= AllocatePageTableMemory (1);
304 ASSERT(PageDirectoryPointerEntry
!= NULL
);
305 ZeroMem (PageDirectoryPointerEntry
, EFI_PAGES_TO_SIZE(1));
307 *PageMapLevel4Entry
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
310 if (m1GPageTableSupport
) {
311 PageDirectory1GEntry
= PageDirectoryPointerEntry
;
312 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
313 if (IndexOfPml4Entries
== 0 && IndexOfPageDirectoryEntries
< 4) {
315 // Skip the < 4G entries
320 // Fill in the Page Directory entries
322 *PageDirectory1GEntry
= PageAddress
| mAddressEncMask
| IA32_PG_PS
| PAGE_ATTRIBUTE_BITS
;
325 PageAddress
= BASE_4GB
;
326 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< (NumberOfPml4EntriesNeeded
== 1 ? NumberOfPdpEntriesNeeded
: 512); IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
327 if (IndexOfPml4Entries
== 0 && IndexOfPdpEntries
< 4) {
329 // Skip the < 4G entries
334 // Each Directory Pointer entries points to a page of Page Directory entires.
335 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
337 PageDirectoryEntry
= (UINT64
*) ((*PageDirectoryPointerEntry
) & ~mAddressEncMask
& gPhyMask
);
338 if (PageDirectoryEntry
== NULL
) {
339 PageDirectoryEntry
= AllocatePageTableMemory (1);
340 ASSERT(PageDirectoryEntry
!= NULL
);
341 ZeroMem (PageDirectoryEntry
, EFI_PAGES_TO_SIZE(1));
344 // Fill in a Page Directory Pointer Entries
346 *PageDirectoryPointerEntry
= (UINT64
)(UINTN
)PageDirectoryEntry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
349 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
351 // Fill in the Page Directory entries
353 *PageDirectoryEntry
= PageAddress
| mAddressEncMask
| IA32_PG_PS
| PAGE_ATTRIBUTE_BITS
;
362 Create PageTable for SMM use.
364 @return The address of PML4 (to set CR3).
372 EFI_PHYSICAL_ADDRESS Pages
;
374 LIST_ENTRY
*FreePage
;
376 UINTN PageFaultHandlerHookAddress
;
377 IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
;
383 // Initialize spin lock
385 InitializeSpinLock (mPFLock
);
387 mCpuSmmRestrictedMemoryAccess
= PcdGetBool (PcdCpuSmmRestrictedMemoryAccess
);
388 m1GPageTableSupport
= Is1GPageSupport ();
389 m5LevelPagingNeeded
= Is5LevelPagingNeeded ();
390 mPhysicalAddressBits
= CalculateMaximumSupportAddress ();
391 PatchInstructionX86 (gPatch5LevelPagingNeeded
, m5LevelPagingNeeded
, 1);
392 DEBUG ((DEBUG_INFO
, "5LevelPaging Needed - %d\n", m5LevelPagingNeeded
));
393 DEBUG ((DEBUG_INFO
, "1GPageTable Support - %d\n", m1GPageTableSupport
));
394 DEBUG ((DEBUG_INFO
, "PcdCpuSmmRestrictedMemoryAccess - %d\n", mCpuSmmRestrictedMemoryAccess
));
395 DEBUG ((DEBUG_INFO
, "PhysicalAddressBits - %d\n", mPhysicalAddressBits
));
397 // Generate PAE page table for the first 4GB memory space
399 Pages
= Gen4GPageTable (FALSE
);
402 // Set IA32_PG_PMNT bit to mask this entry
404 PTEntry
= (UINT64
*)(UINTN
)Pages
;
405 for (Index
= 0; Index
< 4; Index
++) {
406 PTEntry
[Index
] |= IA32_PG_PMNT
;
410 // Fill Page-Table-Level4 (PML4) entry
412 Pml4Entry
= (UINT64
*)AllocatePageTableMemory (1);
413 ASSERT (Pml4Entry
!= NULL
);
414 *Pml4Entry
= Pages
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
415 ZeroMem (Pml4Entry
+ 1, EFI_PAGE_SIZE
- sizeof (*Pml4Entry
));
418 // Set sub-entries number
420 SetSubEntriesNum (Pml4Entry
, 3);
423 if (m5LevelPagingNeeded
) {
427 Pml5Entry
= (UINT64
*)AllocatePageTableMemory (1);
428 ASSERT (Pml5Entry
!= NULL
);
429 *Pml5Entry
= (UINTN
) Pml4Entry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
430 ZeroMem (Pml5Entry
+ 1, EFI_PAGE_SIZE
- sizeof (*Pml5Entry
));
432 // Set sub-entries number
434 SetSubEntriesNum (Pml5Entry
, 1);
438 if (mCpuSmmRestrictedMemoryAccess
) {
440 // When access to non-SMRAM memory is restricted, create page table
441 // that covers all memory space.
443 SetStaticPageTable ((UINTN
)PTEntry
, mPhysicalAddressBits
);
446 // Add pages to page pool
448 FreePage
= (LIST_ENTRY
*)AllocatePageTableMemory (PAGE_TABLE_PAGES
);
449 ASSERT (FreePage
!= NULL
);
450 for (Index
= 0; Index
< PAGE_TABLE_PAGES
; Index
++) {
451 InsertTailList (&mPagePool
, FreePage
);
452 FreePage
+= EFI_PAGE_SIZE
/ sizeof (*FreePage
);
456 if (FeaturePcdGet (PcdCpuSmmProfileEnable
) ||
457 HEAP_GUARD_NONSTOP_MODE
||
458 NULL_DETECTION_NONSTOP_MODE
) {
460 // Set own Page Fault entry instead of the default one, because SMM Profile
461 // feature depends on IRET instruction to do Single Step
463 PageFaultHandlerHookAddress
= (UINTN
)PageFaultIdtHandlerSmmProfile
;
464 IdtEntry
= (IA32_IDT_GATE_DESCRIPTOR
*) gcSmiIdtr
.Base
;
465 IdtEntry
+= EXCEPT_IA32_PAGE_FAULT
;
466 IdtEntry
->Bits
.OffsetLow
= (UINT16
)PageFaultHandlerHookAddress
;
467 IdtEntry
->Bits
.Reserved_0
= 0;
468 IdtEntry
->Bits
.GateType
= IA32_IDT_GATE_TYPE_INTERRUPT_32
;
469 IdtEntry
->Bits
.OffsetHigh
= (UINT16
)(PageFaultHandlerHookAddress
>> 16);
470 IdtEntry
->Bits
.OffsetUpper
= (UINT32
)(PageFaultHandlerHookAddress
>> 32);
471 IdtEntry
->Bits
.Reserved_1
= 0;
474 // Register Smm Page Fault Handler
476 Status
= SmmRegisterExceptionHandler (&mSmmCpuService
, EXCEPT_IA32_PAGE_FAULT
, SmiPFHandler
);
477 ASSERT_EFI_ERROR (Status
);
481 // Additional SMM IDT initialization for SMM stack guard
483 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
484 InitializeIDTSmmStackGuard ();
488 // Return the address of PML4/PML5 (to set CR3)
490 return (UINT32
)(UINTN
)PTEntry
;
494 Set access record in entry.
496 @param[in, out] Entry Pointer to entry
497 @param[in] Acc Access record value
502 IN OUT UINT64
*Entry
,
507 // Access record is saved in BIT9 to BIT11 (reserved field) in Entry
509 *Entry
= BitFieldWrite64 (*Entry
, 9, 11, Acc
);
513 Return access record in entry.
515 @param[in] Entry Pointer to entry
517 @return Access record value.
526 // Access record is saved in BIT9 to BIT11 (reserved field) in Entry
528 return BitFieldRead64 (*Entry
, 9, 11);
532 Return and update the access record in entry.
534 @param[in, out] Entry Pointer to entry
536 @return Access record value.
546 Acc
= GetAccNum (Entry
);
547 if ((*Entry
& IA32_PG_A
) != 0) {
549 // If this entry has been accessed, clear access flag in Entry and update access record
550 // to the initial value 7, adding ACC_MAX_BIT is to make it larger than others
552 *Entry
&= ~(UINT64
)(UINTN
)IA32_PG_A
;
553 SetAccNum (Entry
, 0x7);
554 return (0x7 + ACC_MAX_BIT
);
558 // If the access record is not the smallest value 0, minus 1 and update the access record field
560 SetAccNum (Entry
, Acc
- 1);
567 Reclaim free pages for PageFault handler.
569 Search the whole entries tree to find the leaf entry that has the smallest
570 access record value. Insert the page pointed by this leaf entry into the
571 page pool. And check its upper entries if need to be inserted into the page
595 UINT64 SubEntriesNum
;
598 UINT64
*ReleasePageAddress
;
600 BOOLEAN Enable5LevelPaging
;
602 UINT64 PFAddressPml5Index
;
603 UINT64 PFAddressPml4Index
;
604 UINT64 PFAddressPdptIndex
;
605 UINT64 PFAddressPdtIndex
;
616 ReleasePageAddress
= 0;
617 PFAddress
= AsmReadCr2 ();
618 PFAddressPml5Index
= BitFieldRead64 (PFAddress
, 48, 48 + 8);
619 PFAddressPml4Index
= BitFieldRead64 (PFAddress
, 39, 39 + 8);
620 PFAddressPdptIndex
= BitFieldRead64 (PFAddress
, 30, 30 + 8);
621 PFAddressPdtIndex
= BitFieldRead64 (PFAddress
, 21, 21 + 8);
623 Cr4
.UintN
= AsmReadCr4 ();
624 Enable5LevelPaging
= (BOOLEAN
) (Cr4
.Bits
.LA57
== 1);
625 Pml5
= (UINT64
*)(UINTN
)(AsmReadCr3 () & gPhyMask
);
627 if (!Enable5LevelPaging
) {
629 // Create one fake PML5 entry for 4-Level Paging
630 // so that the page table parsing logic only handles 5-Level page structure.
632 Pml5Entry
= (UINTN
) Pml5
| IA32_PG_P
;
637 // First, find the leaf entry has the smallest access record value
639 for (Pml5Index
= 0; Pml5Index
< (Enable5LevelPaging
? (EFI_PAGE_SIZE
/ sizeof (*Pml4
)) : 1); Pml5Index
++) {
640 if ((Pml5
[Pml5Index
] & IA32_PG_P
) == 0 || (Pml5
[Pml5Index
] & IA32_PG_PMNT
) != 0) {
642 // If the PML5 entry is not present or is masked, skip it
646 Pml4
= (UINT64
*)(UINTN
)(Pml5
[Pml5Index
] & gPhyMask
);
647 for (Pml4Index
= 0; Pml4Index
< EFI_PAGE_SIZE
/ sizeof (*Pml4
); Pml4Index
++) {
648 if ((Pml4
[Pml4Index
] & IA32_PG_P
) == 0 || (Pml4
[Pml4Index
] & IA32_PG_PMNT
) != 0) {
650 // If the PML4 entry is not present or is masked, skip it
654 Pdpt
= (UINT64
*)(UINTN
)(Pml4
[Pml4Index
] & ~mAddressEncMask
& gPhyMask
);
656 for (PdptIndex
= 0; PdptIndex
< EFI_PAGE_SIZE
/ sizeof (*Pdpt
); PdptIndex
++) {
657 if ((Pdpt
[PdptIndex
] & IA32_PG_P
) == 0 || (Pdpt
[PdptIndex
] & IA32_PG_PMNT
) != 0) {
659 // If the PDPT entry is not present or is masked, skip it
661 if ((Pdpt
[PdptIndex
] & IA32_PG_PMNT
) != 0) {
663 // If the PDPT entry is masked, we will ignore checking the PML4 entry
669 if ((Pdpt
[PdptIndex
] & IA32_PG_PS
) == 0) {
671 // It's not 1-GByte pages entry, it should be a PDPT entry,
672 // we will not check PML4 entry more
675 Pdt
= (UINT64
*)(UINTN
)(Pdpt
[PdptIndex
] & ~mAddressEncMask
& gPhyMask
);
677 for (PdtIndex
= 0; PdtIndex
< EFI_PAGE_SIZE
/ sizeof(*Pdt
); PdtIndex
++) {
678 if ((Pdt
[PdtIndex
] & IA32_PG_P
) == 0 || (Pdt
[PdtIndex
] & IA32_PG_PMNT
) != 0) {
680 // If the PD entry is not present or is masked, skip it
682 if ((Pdt
[PdtIndex
] & IA32_PG_PMNT
) != 0) {
684 // If the PD entry is masked, we will not PDPT entry more
690 if ((Pdt
[PdtIndex
] & IA32_PG_PS
) == 0) {
692 // It's not 2 MByte page table entry, it should be PD entry
693 // we will find the entry has the smallest access record value
696 if (PdtIndex
!= PFAddressPdtIndex
|| PdptIndex
!= PFAddressPdptIndex
||
697 Pml4Index
!= PFAddressPml4Index
|| Pml5Index
!= PFAddressPml5Index
) {
698 Acc
= GetAndUpdateAccNum (Pdt
+ PdtIndex
);
701 // If the PD entry has the smallest access record value,
702 // save the Page address to be released
709 ReleasePageAddress
= Pdt
+ PdtIndex
;
716 // If this PDPT entry has no PDT entries pointer to 4 KByte pages,
717 // it should only has the entries point to 2 MByte Pages
719 if (PdptIndex
!= PFAddressPdptIndex
|| Pml4Index
!= PFAddressPml4Index
||
720 Pml5Index
!= PFAddressPml5Index
) {
721 Acc
= GetAndUpdateAccNum (Pdpt
+ PdptIndex
);
724 // If the PDPT entry has the smallest access record value,
725 // save the Page address to be released
732 ReleasePageAddress
= Pdpt
+ PdptIndex
;
740 // If PML4 entry has no the PDPT entry pointer to 2 MByte pages,
741 // it should only has the entries point to 1 GByte Pages
743 if (Pml4Index
!= PFAddressPml4Index
|| Pml5Index
!= PFAddressPml5Index
) {
744 Acc
= GetAndUpdateAccNum (Pml4
+ Pml4Index
);
747 // If the PML4 entry has the smallest access record value,
748 // save the Page address to be released
755 ReleasePageAddress
= Pml4
+ Pml4Index
;
762 // Make sure one PML4/PDPT/PD entry is selected
764 ASSERT (MinAcc
!= (UINT64
)-1);
767 // Secondly, insert the page pointed by this entry into page pool and clear this entry
769 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(*ReleasePageAddress
& ~mAddressEncMask
& gPhyMask
));
770 *ReleasePageAddress
= 0;
773 // Lastly, check this entry's upper entries if need to be inserted into page pool
777 if (MinPdt
!= (UINTN
)-1) {
779 // If 4 KByte Page Table is released, check the PDPT entry
781 Pml4
= (UINT64
*) (UINTN
) (Pml5
[MinPml5
] & gPhyMask
);
782 Pdpt
= (UINT64
*)(UINTN
)(Pml4
[MinPml4
] & ~mAddressEncMask
& gPhyMask
);
783 SubEntriesNum
= GetSubEntriesNum(Pdpt
+ MinPdpt
);
784 if (SubEntriesNum
== 0 &&
785 (MinPdpt
!= PFAddressPdptIndex
|| MinPml4
!= PFAddressPml4Index
|| MinPml5
!= PFAddressPml5Index
)) {
787 // Release the empty Page Directory table if there was no more 4 KByte Page Table entry
788 // clear the Page directory entry
790 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(Pdpt
[MinPdpt
] & ~mAddressEncMask
& gPhyMask
));
793 // Go on checking the PML4 table
799 // Update the sub-entries filed in PDPT entry and exit
801 SetSubEntriesNum (Pdpt
+ MinPdpt
, (SubEntriesNum
- 1) & 0x1FF);
804 if (MinPdpt
!= (UINTN
)-1) {
806 // One 2MB Page Table is released or Page Directory table is released, check the PML4 entry
808 SubEntriesNum
= GetSubEntriesNum (Pml4
+ MinPml4
);
809 if (SubEntriesNum
== 0 && (MinPml4
!= PFAddressPml4Index
|| MinPml5
!= PFAddressPml5Index
)) {
811 // Release the empty PML4 table if there was no more 1G KByte Page Table entry
812 // clear the Page directory entry
814 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(Pml4
[MinPml4
] & ~mAddressEncMask
& gPhyMask
));
820 // Update the sub-entries filed in PML4 entry and exit
822 SetSubEntriesNum (Pml4
+ MinPml4
, (SubEntriesNum
- 1) & 0x1FF);
826 // PLM4 table has been released before, exit it
833 Allocate free Page for PageFault handler use.
835 @return Page address.
845 if (IsListEmpty (&mPagePool
)) {
847 // If page pool is empty, reclaim the used pages and insert one into page pool
853 // Get one free page and remove it from page pool
855 RetVal
= (UINT64
)(UINTN
)mPagePool
.ForwardLink
;
856 RemoveEntryList (mPagePool
.ForwardLink
);
858 // Clean this page and return
860 ZeroMem ((VOID
*)(UINTN
)RetVal
, EFI_PAGE_SIZE
);
865 Page Fault handler for SMM use.
869 SmiDefaultPFHandler (
874 UINT64
*PageTableTop
;
880 SMM_PAGE_SIZE_TYPE PageSize
;
885 BOOLEAN Enable5LevelPaging
;
889 // Set default SMM page attribute
891 PageSize
= SmmPageSize2M
;
896 PageTableTop
= (UINT64
*)(AsmReadCr3 () & gPhyMask
);
897 PFAddress
= AsmReadCr2 ();
899 Cr4
.UintN
= AsmReadCr4 ();
900 Enable5LevelPaging
= (BOOLEAN
) (Cr4
.Bits
.LA57
!= 0);
902 Status
= GetPlatformPageTableAttribute (PFAddress
, &PageSize
, &NumOfPages
, &PageAttribute
);
904 // If platform not support page table attribute, set default SMM page attribute
906 if (Status
!= EFI_SUCCESS
) {
907 PageSize
= SmmPageSize2M
;
911 if (PageSize
>= MaxSmmPageSizeType
) {
912 PageSize
= SmmPageSize2M
;
914 if (NumOfPages
> 512) {
921 // BIT12 to BIT20 is Page Table index
927 // BIT21 to BIT29 is Page Directory index
930 PageAttribute
|= (UINTN
)IA32_PG_PS
;
933 if (!m1GPageTableSupport
) {
934 DEBUG ((DEBUG_ERROR
, "1-GByte pages is not supported!"));
938 // BIT30 to BIT38 is Page Directory Pointer Table index
941 PageAttribute
|= (UINTN
)IA32_PG_PS
;
948 // If execute-disable is enabled, set NX bit
951 PageAttribute
|= IA32_PG_NX
;
954 for (Index
= 0; Index
< NumOfPages
; Index
++) {
955 PageTable
= PageTableTop
;
957 for (StartBit
= Enable5LevelPaging
? 48 : 39; StartBit
> EndBit
; StartBit
-= 9) {
958 PTIndex
= BitFieldRead64 (PFAddress
, StartBit
, StartBit
+ 8);
959 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
961 // If the entry is not present, allocate one page from page pool for it
963 PageTable
[PTIndex
] = AllocPage () | mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
966 // Save the upper entry address
968 UpperEntry
= PageTable
+ PTIndex
;
971 // BIT9 to BIT11 of entry is used to save access record,
972 // initialize value is 7
974 PageTable
[PTIndex
] |= (UINT64
)IA32_PG_A
;
975 SetAccNum (PageTable
+ PTIndex
, 7);
976 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & ~mAddressEncMask
& gPhyMask
);
979 PTIndex
= BitFieldRead64 (PFAddress
, StartBit
, StartBit
+ 8);
980 if ((PageTable
[PTIndex
] & IA32_PG_P
) != 0) {
982 // Check if the entry has already existed, this issue may occur when the different
983 // size page entries created under the same entry
985 DEBUG ((DEBUG_ERROR
, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable
, PTIndex
, PageTable
[PTIndex
]));
986 DEBUG ((DEBUG_ERROR
, "New page table overlapped with old page table!\n"));
990 // Fill the new entry
992 PageTable
[PTIndex
] = ((PFAddress
| mAddressEncMask
) & gPhyMask
& ~((1ull << EndBit
) - 1)) |
993 PageAttribute
| IA32_PG_A
| PAGE_ATTRIBUTE_BITS
;
994 if (UpperEntry
!= NULL
) {
995 SetSubEntriesNum (UpperEntry
, (GetSubEntriesNum (UpperEntry
) + 1) & 0x1FF);
998 // Get the next page address if we need to create more page tables
1000 PFAddress
+= (1ull << EndBit
);
1005 ThePage Fault handler wrapper for SMM use.
1007 @param InterruptType Defines the type of interrupt or exception that
1008 occurred on the processor.This parameter is processor architecture specific.
1009 @param SystemContext A pointer to the processor context when
1010 the interrupt occurred on the processor.
1015 IN EFI_EXCEPTION_TYPE InterruptType
,
1016 IN EFI_SYSTEM_CONTEXT SystemContext
1020 UINTN GuardPageAddress
;
1021 UINTN ShadowStackGuardPageAddress
;
1024 ASSERT (InterruptType
== EXCEPT_IA32_PAGE_FAULT
);
1026 AcquireSpinLock (mPFLock
);
1028 PFAddress
= AsmReadCr2 ();
1030 if (mCpuSmmRestrictedMemoryAccess
&& (PFAddress
>= LShiftU64 (1, (mPhysicalAddressBits
- 1)))) {
1031 DumpCpuContext (InterruptType
, SystemContext
);
1032 DEBUG ((DEBUG_ERROR
, "Do not support address 0x%lx by processor!\n", PFAddress
));
1038 // If a page fault occurs in SMRAM range, it might be in a SMM stack/shadow stack guard page,
1039 // or SMM page protection violation.
1041 if ((PFAddress
>= mCpuHotPlugData
.SmrrBase
) &&
1042 (PFAddress
< (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
))) {
1043 DumpCpuContext (InterruptType
, SystemContext
);
1044 CpuIndex
= GetCpuIndex ();
1045 GuardPageAddress
= (mSmmStackArrayBase
+ EFI_PAGE_SIZE
+ CpuIndex
* (mSmmStackSize
+ mSmmShadowStackSize
));
1046 ShadowStackGuardPageAddress
= (mSmmStackArrayBase
+ mSmmStackSize
+ EFI_PAGE_SIZE
+ CpuIndex
* (mSmmStackSize
+ mSmmShadowStackSize
));
1047 if ((FeaturePcdGet (PcdCpuSmmStackGuard
)) &&
1048 (PFAddress
>= GuardPageAddress
) &&
1049 (PFAddress
< (GuardPageAddress
+ EFI_PAGE_SIZE
))) {
1050 DEBUG ((DEBUG_ERROR
, "SMM stack overflow!\n"));
1051 } else if ((FeaturePcdGet (PcdCpuSmmStackGuard
)) &&
1052 (mSmmShadowStackSize
> 0) &&
1053 (PFAddress
>= ShadowStackGuardPageAddress
) &&
1054 (PFAddress
< (ShadowStackGuardPageAddress
+ EFI_PAGE_SIZE
))) {
1055 DEBUG ((DEBUG_ERROR
, "SMM shadow stack overflow!\n"));
1057 if ((SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_ID
) != 0) {
1058 DEBUG ((DEBUG_ERROR
, "SMM exception at execution (0x%lx)\n", PFAddress
));
1060 DumpModuleInfoByIp (*(UINTN
*)(UINTN
)SystemContext
.SystemContextX64
->Rsp
);
1063 DEBUG ((DEBUG_ERROR
, "SMM exception at access (0x%lx)\n", PFAddress
));
1065 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextX64
->Rip
);
1069 if (HEAP_GUARD_NONSTOP_MODE
) {
1070 GuardPagePFHandler (SystemContext
.SystemContextX64
->ExceptionData
);
1079 // If a page fault occurs in non-SMRAM range.
1081 if ((PFAddress
< mCpuHotPlugData
.SmrrBase
) ||
1082 (PFAddress
>= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
)) {
1083 if ((SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_ID
) != 0) {
1084 DumpCpuContext (InterruptType
, SystemContext
);
1085 DEBUG ((DEBUG_ERROR
, "Code executed on IP(0x%lx) out of SMM range after SMM is locked!\n", PFAddress
));
1087 DumpModuleInfoByIp (*(UINTN
*)(UINTN
)SystemContext
.SystemContextX64
->Rsp
);
1094 // If NULL pointer was just accessed
1096 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask
) & BIT1
) != 0 &&
1097 (PFAddress
< EFI_PAGE_SIZE
)) {
1098 DumpCpuContext (InterruptType
, SystemContext
);
1099 DEBUG ((DEBUG_ERROR
, "!!! NULL pointer access !!!\n"));
1101 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextX64
->Rip
);
1104 if (NULL_DETECTION_NONSTOP_MODE
) {
1105 GuardPagePFHandler (SystemContext
.SystemContextX64
->ExceptionData
);
1113 if (mCpuSmmRestrictedMemoryAccess
&& IsSmmCommBufferForbiddenAddress (PFAddress
)) {
1114 DumpCpuContext (InterruptType
, SystemContext
);
1115 DEBUG ((DEBUG_ERROR
, "Access SMM communication forbidden address (0x%lx)!\n", PFAddress
));
1117 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextX64
->Rip
);
1124 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
1125 SmmProfilePFHandler (
1126 SystemContext
.SystemContextX64
->Rip
,
1127 SystemContext
.SystemContextX64
->ExceptionData
1130 SmiDefaultPFHandler ();
1134 ReleaseSpinLock (mPFLock
);
1138 This function sets memory attribute for page table.
1141 SetPageTableAttributes (
1149 UINT64
*L1PageTable
;
1150 UINT64
*L2PageTable
;
1151 UINT64
*L3PageTable
;
1152 UINT64
*L4PageTable
;
1153 UINT64
*L5PageTable
;
1154 UINTN PageTableBase
;
1156 BOOLEAN PageTableSplitted
;
1158 BOOLEAN Enable5LevelPaging
;
1161 // Don't mark page table memory as read-only if
1162 // - no restriction on access to non-SMRAM memory; or
1163 // - SMM heap guard feature enabled; or
1164 // BIT2: SMM page guard enabled
1165 // BIT3: SMM pool guard enabled
1166 // - SMM profile feature enabled
1168 if (!mCpuSmmRestrictedMemoryAccess
||
1169 ((PcdGet8 (PcdHeapGuardPropertyMask
) & (BIT3
| BIT2
)) != 0) ||
1170 FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
1172 // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time.
1174 ASSERT (!(mCpuSmmRestrictedMemoryAccess
&&
1175 (PcdGet8 (PcdHeapGuardPropertyMask
) & (BIT3
| BIT2
)) != 0));
1178 // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time.
1180 ASSERT (!(mCpuSmmRestrictedMemoryAccess
&& FeaturePcdGet (PcdCpuSmmProfileEnable
)));
1184 DEBUG ((DEBUG_INFO
, "SetPageTableAttributes\n"));
1187 // Disable write protection, because we need mark page table to be write protected.
1188 // We need *write* page table memory, to mark itself to be *read only*.
1190 CetEnabled
= ((AsmReadCr4() & CR4_CET_ENABLE
) != 0) ? TRUE
: FALSE
;
1193 // CET must be disabled if WP is disabled.
1197 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP
);
1200 DEBUG ((DEBUG_INFO
, "Start...\n"));
1201 PageTableSplitted
= FALSE
;
1204 GetPageTable (&PageTableBase
, &Enable5LevelPaging
);
1206 if (Enable5LevelPaging
) {
1207 L5PageTable
= (UINT64
*)PageTableBase
;
1208 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)PageTableBase
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
1209 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
1212 for (Index5
= 0; Index5
< (Enable5LevelPaging
? SIZE_4KB
/sizeof(UINT64
) : 1); Index5
++) {
1213 if (Enable5LevelPaging
) {
1214 L4PageTable
= (UINT64
*)(UINTN
)(L5PageTable
[Index5
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
1215 if (L4PageTable
== NULL
) {
1219 L4PageTable
= (UINT64
*)PageTableBase
;
1221 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L4PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
1222 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
1224 for (Index4
= 0; Index4
< SIZE_4KB
/sizeof(UINT64
); Index4
++) {
1225 L3PageTable
= (UINT64
*)(UINTN
)(L4PageTable
[Index4
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
1226 if (L3PageTable
== NULL
) {
1230 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L3PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
1231 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
1233 for (Index3
= 0; Index3
< SIZE_4KB
/sizeof(UINT64
); Index3
++) {
1234 if ((L3PageTable
[Index3
] & IA32_PG_PS
) != 0) {
1238 L2PageTable
= (UINT64
*)(UINTN
)(L3PageTable
[Index3
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
1239 if (L2PageTable
== NULL
) {
1243 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L2PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
1244 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
1246 for (Index2
= 0; Index2
< SIZE_4KB
/sizeof(UINT64
); Index2
++) {
1247 if ((L2PageTable
[Index2
] & IA32_PG_PS
) != 0) {
1251 L1PageTable
= (UINT64
*)(UINTN
)(L2PageTable
[Index2
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
1252 if (L1PageTable
== NULL
) {
1255 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L1PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
1256 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
1261 } while (PageTableSplitted
);
1264 // Enable write protection, after page table updated.
1266 AsmWriteCr0 (AsmReadCr0() | CR0_WP
);
1278 This function reads CR2 register when on-demand paging is enabled.
1280 @param[out] *Cr2 Pointer to variable to hold CR2 register value.
1287 if (!mCpuSmmRestrictedMemoryAccess
) {
1289 // On-demand paging is enabled when access to non-SMRAM is not restricted.
1291 *Cr2
= AsmReadCr2 ();
1296 This function restores CR2 register when on-demand paging is enabled.
1298 @param[in] Cr2 Value to write into CR2 register.
1305 if (!mCpuSmmRestrictedMemoryAccess
) {
1307 // On-demand paging is enabled when access to non-SMRAM is not restricted.
1314 Return whether access to non-SMRAM is restricted.
1316 @retval TRUE Access to non-SMRAM is restricted.
1317 @retval FALSE Access to non-SMRAM is not restricted.
1320 IsRestrictedMemoryAccess (
1324 return mCpuSmmRestrictedMemoryAccess
;