2 Page Fault (#PF) handler for X64 processors
4 Copyright (c) 2009 - 2016, 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 UINT8 mPhysicalAddressBits
;
25 BOOLEAN mCpuSmmStaticPageTable
;
28 Check if 1-GByte pages is supported by processor or not.
30 @retval TRUE 1-GByte pages is supported.
31 @retval FALSE 1-GByte pages is not supported.
42 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
43 if (RegEax
>= 0x80000001) {
44 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
45 if ((RegEdx
& BIT26
) != 0) {
53 Set sub-entries number in entry.
55 @param[in, out] Entry Pointer to entry
56 @param[in] SubEntryNum Sub-entries number based on 0:
57 0 means there is 1 sub-entry under this entry
58 0x1ff means there is 512 sub-entries under this entry
68 // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
70 *Entry
= BitFieldWrite64 (*Entry
, 52, 60, SubEntryNum
);
74 Return sub-entries number in entry.
76 @param[in] Entry Pointer to entry
78 @return Sub-entries number based on 0:
79 0 means there is 1 sub-entry under this entry
80 0x1ff means there is 512 sub-entries under this entry
88 // Sub-entries number is saved in BIT52 to BIT60 (reserved field) in Entry
90 return BitFieldRead64 (*Entry
, 52, 60);
94 Calculate the maximum support address.
96 @return the maximum support address.
99 CalculateMaximumSupportAddress (
104 UINT8 PhysicalAddressBits
;
108 // Get physical address bits supported.
110 Hob
= GetFirstHob (EFI_HOB_TYPE_CPU
);
112 PhysicalAddressBits
= ((EFI_HOB_CPU
*) Hob
)->SizeOfMemorySpace
;
114 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
115 if (RegEax
>= 0x80000008) {
116 AsmCpuid (0x80000008, &RegEax
, NULL
, NULL
, NULL
);
117 PhysicalAddressBits
= (UINT8
) RegEax
;
119 PhysicalAddressBits
= 36;
124 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.
126 ASSERT (PhysicalAddressBits
<= 52);
127 if (PhysicalAddressBits
> 48) {
128 PhysicalAddressBits
= 48;
130 return PhysicalAddressBits
;
134 Set static page table.
136 @param[in] PageTable Address of page table.
144 UINTN NumberOfPml4EntriesNeeded
;
145 UINTN NumberOfPdpEntriesNeeded
;
146 UINTN IndexOfPml4Entries
;
147 UINTN IndexOfPdpEntries
;
148 UINTN IndexOfPageDirectoryEntries
;
149 UINT64
*PageMapLevel4Entry
;
151 UINT64
*PageDirectoryPointerEntry
;
152 UINT64
*PageDirectory1GEntry
;
153 UINT64
*PageDirectoryEntry
;
155 if (mPhysicalAddressBits
<= 39 ) {
156 NumberOfPml4EntriesNeeded
= 1;
157 NumberOfPdpEntriesNeeded
= (UINT32
)LShiftU64 (1, (mPhysicalAddressBits
- 30));
159 NumberOfPml4EntriesNeeded
= (UINT32
)LShiftU64 (1, (mPhysicalAddressBits
- 39));
160 NumberOfPdpEntriesNeeded
= 512;
164 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.
166 PageMap
= (VOID
*) PageTable
;
168 PageMapLevel4Entry
= PageMap
;
170 for (IndexOfPml4Entries
= 0; IndexOfPml4Entries
< NumberOfPml4EntriesNeeded
; IndexOfPml4Entries
++, PageMapLevel4Entry
++) {
172 // Each PML4 entry points to a page of Page Directory Pointer entries.
174 PageDirectoryPointerEntry
= (UINT64
*) ((*PageMapLevel4Entry
) & ~mAddressEncMask
& gPhyMask
);
175 if (PageDirectoryPointerEntry
== NULL
) {
176 PageDirectoryPointerEntry
= AllocatePageTableMemory (1);
177 ASSERT(PageDirectoryPointerEntry
!= NULL
);
178 ZeroMem (PageDirectoryPointerEntry
, EFI_PAGES_TO_SIZE(1));
180 *PageMapLevel4Entry
= (UINT64
)(UINTN
)PageDirectoryPointerEntry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
183 if (m1GPageTableSupport
) {
184 PageDirectory1GEntry
= PageDirectoryPointerEntry
;
185 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectory1GEntry
++, PageAddress
+= SIZE_1GB
) {
186 if (IndexOfPml4Entries
== 0 && IndexOfPageDirectoryEntries
< 4) {
188 // Skip the < 4G entries
193 // Fill in the Page Directory entries
195 *PageDirectory1GEntry
= PageAddress
| mAddressEncMask
| IA32_PG_PS
| PAGE_ATTRIBUTE_BITS
;
198 PageAddress
= BASE_4GB
;
199 for (IndexOfPdpEntries
= 0; IndexOfPdpEntries
< NumberOfPdpEntriesNeeded
; IndexOfPdpEntries
++, PageDirectoryPointerEntry
++) {
200 if (IndexOfPml4Entries
== 0 && IndexOfPdpEntries
< 4) {
202 // Skip the < 4G entries
207 // Each Directory Pointer entries points to a page of Page Directory entires.
208 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.
210 PageDirectoryEntry
= (UINT64
*) ((*PageDirectoryPointerEntry
) & ~mAddressEncMask
& gPhyMask
);
211 if (PageDirectoryEntry
== NULL
) {
212 PageDirectoryEntry
= AllocatePageTableMemory (1);
213 ASSERT(PageDirectoryEntry
!= NULL
);
214 ZeroMem (PageDirectoryEntry
, EFI_PAGES_TO_SIZE(1));
217 // Fill in a Page Directory Pointer Entries
219 *PageDirectoryPointerEntry
= (UINT64
)(UINTN
)PageDirectoryEntry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
222 for (IndexOfPageDirectoryEntries
= 0; IndexOfPageDirectoryEntries
< 512; IndexOfPageDirectoryEntries
++, PageDirectoryEntry
++, PageAddress
+= SIZE_2MB
) {
224 // Fill in the Page Directory entries
226 *PageDirectoryEntry
= PageAddress
| mAddressEncMask
| IA32_PG_PS
| PAGE_ATTRIBUTE_BITS
;
234 Create PageTable for SMM use.
236 @return The address of PML4 (to set CR3).
244 EFI_PHYSICAL_ADDRESS Pages
;
246 LIST_ENTRY
*FreePage
;
248 UINTN PageFaultHandlerHookAddress
;
249 IA32_IDT_GATE_DESCRIPTOR
*IdtEntry
;
253 // Initialize spin lock
255 InitializeSpinLock (mPFLock
);
257 mCpuSmmStaticPageTable
= PcdGetBool (PcdCpuSmmStaticPageTable
);
258 m1GPageTableSupport
= Is1GPageSupport ();
259 DEBUG ((DEBUG_INFO
, "1GPageTableSupport - 0x%x\n", m1GPageTableSupport
));
260 DEBUG ((DEBUG_INFO
, "PcdCpuSmmStaticPageTable - 0x%x\n", mCpuSmmStaticPageTable
));
262 mPhysicalAddressBits
= CalculateMaximumSupportAddress ();
263 DEBUG ((DEBUG_INFO
, "PhysicalAddressBits - 0x%x\n", mPhysicalAddressBits
));
265 // Generate PAE page table for the first 4GB memory space
267 Pages
= Gen4GPageTable (FALSE
);
270 // Set IA32_PG_PMNT bit to mask this entry
272 PTEntry
= (UINT64
*)(UINTN
)Pages
;
273 for (Index
= 0; Index
< 4; Index
++) {
274 PTEntry
[Index
] |= IA32_PG_PMNT
;
278 // Fill Page-Table-Level4 (PML4) entry
280 PTEntry
= (UINT64
*)AllocatePageTableMemory (1);
281 ASSERT (PTEntry
!= NULL
);
282 *PTEntry
= Pages
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
283 ZeroMem (PTEntry
+ 1, EFI_PAGE_SIZE
- sizeof (*PTEntry
));
286 // Set sub-entries number
288 SetSubEntriesNum (PTEntry
, 3);
290 if (mCpuSmmStaticPageTable
) {
291 SetStaticPageTable ((UINTN
)PTEntry
);
294 // Add pages to page pool
296 FreePage
= (LIST_ENTRY
*)AllocatePageTableMemory (PAGE_TABLE_PAGES
);
297 ASSERT (FreePage
!= NULL
);
298 for (Index
= 0; Index
< PAGE_TABLE_PAGES
; Index
++) {
299 InsertTailList (&mPagePool
, FreePage
);
300 FreePage
+= EFI_PAGE_SIZE
/ sizeof (*FreePage
);
304 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
306 // Set own Page Fault entry instead of the default one, because SMM Profile
307 // feature depends on IRET instruction to do Single Step
309 PageFaultHandlerHookAddress
= (UINTN
)PageFaultIdtHandlerSmmProfile
;
310 IdtEntry
= (IA32_IDT_GATE_DESCRIPTOR
*) gcSmiIdtr
.Base
;
311 IdtEntry
+= EXCEPT_IA32_PAGE_FAULT
;
312 IdtEntry
->Bits
.OffsetLow
= (UINT16
)PageFaultHandlerHookAddress
;
313 IdtEntry
->Bits
.Reserved_0
= 0;
314 IdtEntry
->Bits
.GateType
= IA32_IDT_GATE_TYPE_INTERRUPT_32
;
315 IdtEntry
->Bits
.OffsetHigh
= (UINT16
)(PageFaultHandlerHookAddress
>> 16);
316 IdtEntry
->Bits
.OffsetUpper
= (UINT32
)(PageFaultHandlerHookAddress
>> 32);
317 IdtEntry
->Bits
.Reserved_1
= 0;
320 // Register Smm Page Fault Handler
322 Status
= SmmRegisterExceptionHandler (&mSmmCpuService
, EXCEPT_IA32_PAGE_FAULT
, SmiPFHandler
);
323 ASSERT_EFI_ERROR (Status
);
327 // Additional SMM IDT initialization for SMM stack guard
329 if (FeaturePcdGet (PcdCpuSmmStackGuard
)) {
330 InitializeIDTSmmStackGuard ();
334 // Return the address of PML4 (to set CR3)
336 return (UINT32
)(UINTN
)PTEntry
;
340 Set access record in entry.
342 @param[in, out] Entry Pointer to entry
343 @param[in] Acc Access record value
348 IN OUT UINT64
*Entry
,
353 // Access record is saved in BIT9 to BIT11 (reserved field) in Entry
355 *Entry
= BitFieldWrite64 (*Entry
, 9, 11, Acc
);
359 Return access record in entry.
361 @param[in] Entry Pointer to entry
363 @return Access record value.
372 // Access record is saved in BIT9 to BIT11 (reserved field) in Entry
374 return BitFieldRead64 (*Entry
, 9, 11);
378 Return and update the access record in entry.
380 @param[in, out] Entry Pointer to entry
382 @return Access record value.
392 Acc
= GetAccNum (Entry
);
393 if ((*Entry
& IA32_PG_A
) != 0) {
395 // If this entry has been accessed, clear access flag in Entry and update access record
396 // to the initial value 7, adding ACC_MAX_BIT is to make it larger than others
398 *Entry
&= ~(UINT64
)(UINTN
)IA32_PG_A
;
399 SetAccNum (Entry
, 0x7);
400 return (0x7 + ACC_MAX_BIT
);
404 // If the access record is not the smallest value 0, minus 1 and update the access record field
406 SetAccNum (Entry
, Acc
- 1);
413 Reclaim free pages for PageFault handler.
415 Search the whole entries tree to find the leaf entry that has the smallest
416 access record value. Insert the page pointed by this leaf entry into the
417 page pool. And check its upper entries if need to be inserted into the page
437 UINT64 SubEntriesNum
;
440 UINT64
*ReleasePageAddress
;
450 ReleasePageAddress
= 0;
453 // First, find the leaf entry has the smallest access record value
455 Pml4
= (UINT64
*)(UINTN
)(AsmReadCr3 () & gPhyMask
);
456 for (Pml4Index
= 0; Pml4Index
< EFI_PAGE_SIZE
/ sizeof (*Pml4
); Pml4Index
++) {
457 if ((Pml4
[Pml4Index
] & IA32_PG_P
) == 0 || (Pml4
[Pml4Index
] & IA32_PG_PMNT
) != 0) {
459 // If the PML4 entry is not present or is masked, skip it
463 Pdpt
= (UINT64
*)(UINTN
)(Pml4
[Pml4Index
] & ~mAddressEncMask
& gPhyMask
);
465 for (PdptIndex
= 0; PdptIndex
< EFI_PAGE_SIZE
/ sizeof (*Pdpt
); PdptIndex
++) {
466 if ((Pdpt
[PdptIndex
] & IA32_PG_P
) == 0 || (Pdpt
[PdptIndex
] & IA32_PG_PMNT
) != 0) {
468 // If the PDPT entry is not present or is masked, skip it
470 if ((Pdpt
[PdptIndex
] & IA32_PG_PMNT
) != 0) {
472 // If the PDPT entry is masked, we will ignore checking the PML4 entry
478 if ((Pdpt
[PdptIndex
] & IA32_PG_PS
) == 0) {
480 // It's not 1-GByte pages entry, it should be a PDPT entry,
481 // we will not check PML4 entry more
484 Pdt
= (UINT64
*)(UINTN
)(Pdpt
[PdptIndex
] & ~mAddressEncMask
& gPhyMask
);
486 for (PdtIndex
= 0; PdtIndex
< EFI_PAGE_SIZE
/ sizeof(*Pdt
); PdtIndex
++) {
487 if ((Pdt
[PdtIndex
] & IA32_PG_P
) == 0 || (Pdt
[PdtIndex
] & IA32_PG_PMNT
) != 0) {
489 // If the PD entry is not present or is masked, skip it
491 if ((Pdt
[PdtIndex
] & IA32_PG_PMNT
) != 0) {
493 // If the PD entry is masked, we will not PDPT entry more
499 if ((Pdt
[PdtIndex
] & IA32_PG_PS
) == 0) {
501 // It's not 2 MByte page table entry, it should be PD entry
502 // we will find the entry has the smallest access record value
505 Acc
= GetAndUpdateAccNum (Pdt
+ PdtIndex
);
508 // If the PD entry has the smallest access record value,
509 // save the Page address to be released
515 ReleasePageAddress
= Pdt
+ PdtIndex
;
521 // If this PDPT entry has no PDT entries pointer to 4 KByte pages,
522 // it should only has the entries point to 2 MByte Pages
524 Acc
= GetAndUpdateAccNum (Pdpt
+ PdptIndex
);
527 // If the PDPT entry has the smallest access record value,
528 // save the Page address to be released
534 ReleasePageAddress
= Pdpt
+ PdptIndex
;
541 // If PML4 entry has no the PDPT entry pointer to 2 MByte pages,
542 // it should only has the entries point to 1 GByte Pages
544 Acc
= GetAndUpdateAccNum (Pml4
+ Pml4Index
);
547 // If the PML4 entry has the smallest access record value,
548 // save the Page address to be released
554 ReleasePageAddress
= Pml4
+ Pml4Index
;
559 // Make sure one PML4/PDPT/PD entry is selected
561 ASSERT (MinAcc
!= (UINT64
)-1);
564 // Secondly, insert the page pointed by this entry into page pool and clear this entry
566 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(*ReleasePageAddress
& ~mAddressEncMask
& gPhyMask
));
567 *ReleasePageAddress
= 0;
570 // Lastly, check this entry's upper entries if need to be inserted into page pool
574 if (MinPdt
!= (UINTN
)-1) {
576 // If 4 KByte Page Table is released, check the PDPT entry
578 Pdpt
= (UINT64
*)(UINTN
)(Pml4
[MinPml4
] & ~mAddressEncMask
& gPhyMask
);
579 SubEntriesNum
= GetSubEntriesNum(Pdpt
+ MinPdpt
);
580 if (SubEntriesNum
== 0) {
582 // Release the empty Page Directory table if there was no more 4 KByte Page Table entry
583 // clear the Page directory entry
585 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(Pdpt
[MinPdpt
] & ~mAddressEncMask
& gPhyMask
));
588 // Go on checking the PML4 table
594 // Update the sub-entries filed in PDPT entry and exit
596 SetSubEntriesNum (Pdpt
+ MinPdpt
, SubEntriesNum
- 1);
599 if (MinPdpt
!= (UINTN
)-1) {
601 // One 2MB Page Table is released or Page Directory table is released, check the PML4 entry
603 SubEntriesNum
= GetSubEntriesNum (Pml4
+ MinPml4
);
604 if (SubEntriesNum
== 0) {
606 // Release the empty PML4 table if there was no more 1G KByte Page Table entry
607 // clear the Page directory entry
609 InsertTailList (&mPagePool
, (LIST_ENTRY
*)(UINTN
)(Pml4
[MinPml4
] & ~mAddressEncMask
& gPhyMask
));
615 // Update the sub-entries filed in PML4 entry and exit
617 SetSubEntriesNum (Pml4
+ MinPml4
, SubEntriesNum
- 1);
621 // PLM4 table has been released before, exit it
628 Allocate free Page for PageFault handler use.
630 @return Page address.
640 if (IsListEmpty (&mPagePool
)) {
642 // If page pool is empty, reclaim the used pages and insert one into page pool
648 // Get one free page and remove it from page pool
650 RetVal
= (UINT64
)(UINTN
)mPagePool
.ForwardLink
;
651 RemoveEntryList (mPagePool
.ForwardLink
);
653 // Clean this page and return
655 ZeroMem ((VOID
*)(UINTN
)RetVal
, EFI_PAGE_SIZE
);
660 Page Fault handler for SMM use.
664 SmiDefaultPFHandler (
675 SMM_PAGE_SIZE_TYPE PageSize
;
682 // Set default SMM page attribute
684 PageSize
= SmmPageSize2M
;
689 Pml4
= (UINT64
*)(AsmReadCr3 () & gPhyMask
);
690 PFAddress
= AsmReadCr2 ();
692 Status
= GetPlatformPageTableAttribute (PFAddress
, &PageSize
, &NumOfPages
, &PageAttribute
);
694 // If platform not support page table attribute, set default SMM page attribute
696 if (Status
!= EFI_SUCCESS
) {
697 PageSize
= SmmPageSize2M
;
701 if (PageSize
>= MaxSmmPageSizeType
) {
702 PageSize
= SmmPageSize2M
;
704 if (NumOfPages
> 512) {
711 // BIT12 to BIT20 is Page Table index
717 // BIT21 to BIT29 is Page Directory index
720 PageAttribute
|= (UINTN
)IA32_PG_PS
;
723 if (!m1GPageTableSupport
) {
724 DEBUG ((DEBUG_ERROR
, "1-GByte pages is not supported!"));
728 // BIT30 to BIT38 is Page Directory Pointer Table index
731 PageAttribute
|= (UINTN
)IA32_PG_PS
;
738 // If execute-disable is enabled, set NX bit
741 PageAttribute
|= IA32_PG_NX
;
744 for (Index
= 0; Index
< NumOfPages
; Index
++) {
747 for (StartBit
= 39; StartBit
> EndBit
; StartBit
-= 9) {
748 PTIndex
= BitFieldRead64 (PFAddress
, StartBit
, StartBit
+ 8);
749 if ((PageTable
[PTIndex
] & IA32_PG_P
) == 0) {
751 // If the entry is not present, allocate one page from page pool for it
753 PageTable
[PTIndex
] = AllocPage () | mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
756 // Save the upper entry address
758 UpperEntry
= PageTable
+ PTIndex
;
761 // BIT9 to BIT11 of entry is used to save access record,
762 // initialize value is 7
764 PageTable
[PTIndex
] |= (UINT64
)IA32_PG_A
;
765 SetAccNum (PageTable
+ PTIndex
, 7);
766 PageTable
= (UINT64
*)(UINTN
)(PageTable
[PTIndex
] & ~mAddressEncMask
& gPhyMask
);
769 PTIndex
= BitFieldRead64 (PFAddress
, StartBit
, StartBit
+ 8);
770 if ((PageTable
[PTIndex
] & IA32_PG_P
) != 0) {
772 // Check if the entry has already existed, this issue may occur when the different
773 // size page entries created under the same entry
775 DEBUG ((DEBUG_ERROR
, "PageTable = %lx, PTIndex = %x, PageTable[PTIndex] = %lx\n", PageTable
, PTIndex
, PageTable
[PTIndex
]));
776 DEBUG ((DEBUG_ERROR
, "New page table overlapped with old page table!\n"));
780 // Fill the new entry
782 PageTable
[PTIndex
] = ((PFAddress
| mAddressEncMask
) & gPhyMask
& ~((1ull << EndBit
) - 1)) |
783 PageAttribute
| IA32_PG_A
| PAGE_ATTRIBUTE_BITS
;
784 if (UpperEntry
!= NULL
) {
785 SetSubEntriesNum (UpperEntry
, GetSubEntriesNum (UpperEntry
) + 1);
788 // Get the next page address if we need to create more page tables
790 PFAddress
+= (1ull << EndBit
);
795 ThePage Fault handler wrapper for SMM use.
797 @param InterruptType Defines the type of interrupt or exception that
798 occurred on the processor.This parameter is processor architecture specific.
799 @param SystemContext A pointer to the processor context when
800 the interrupt occurred on the processor.
805 IN EFI_EXCEPTION_TYPE InterruptType
,
806 IN EFI_SYSTEM_CONTEXT SystemContext
810 UINTN GuardPageAddress
;
813 ASSERT (InterruptType
== EXCEPT_IA32_PAGE_FAULT
);
815 AcquireSpinLock (mPFLock
);
817 PFAddress
= AsmReadCr2 ();
819 if (mCpuSmmStaticPageTable
&& (PFAddress
>= LShiftU64 (1, (mPhysicalAddressBits
- 1)))) {
820 DEBUG ((DEBUG_ERROR
, "Do not support address 0x%lx by processor!\n", PFAddress
));
825 // If a page fault occurs in SMRAM range, it might be in a SMM stack guard page,
826 // or SMM page protection violation.
828 if ((PFAddress
>= mCpuHotPlugData
.SmrrBase
) &&
829 (PFAddress
< (mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
))) {
830 CpuIndex
= GetCpuIndex ();
831 GuardPageAddress
= (mSmmStackArrayBase
+ EFI_PAGE_SIZE
+ CpuIndex
* mSmmStackSize
);
832 if ((FeaturePcdGet (PcdCpuSmmStackGuard
)) &&
833 (PFAddress
>= GuardPageAddress
) &&
834 (PFAddress
< (GuardPageAddress
+ EFI_PAGE_SIZE
))) {
835 DEBUG ((DEBUG_ERROR
, "SMM stack overflow!\n"));
837 DEBUG ((DEBUG_ERROR
, "SMM exception data - 0x%lx(", SystemContext
.SystemContextX64
->ExceptionData
));
838 DEBUG ((DEBUG_ERROR
, "I:%x, R:%x, U:%x, W:%x, P:%x",
839 (SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_ID
) != 0,
840 (SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_RSVD
) != 0,
841 (SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_US
) != 0,
842 (SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_WR
) != 0,
843 (SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_P
) != 0
845 DEBUG ((DEBUG_ERROR
, ")\n"));
846 if ((SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_ID
) != 0) {
847 DEBUG ((DEBUG_ERROR
, "SMM exception at execution (0x%lx)\n", PFAddress
));
849 DumpModuleInfoByIp (*(UINTN
*)(UINTN
)SystemContext
.SystemContextX64
->Rsp
);
852 DEBUG ((DEBUG_ERROR
, "SMM exception at access (0x%lx)\n", PFAddress
));
854 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextX64
->Rip
);
862 // If a page fault occurs in SMM range
864 if ((PFAddress
< mCpuHotPlugData
.SmrrBase
) ||
865 (PFAddress
>= mCpuHotPlugData
.SmrrBase
+ mCpuHotPlugData
.SmrrSize
)) {
866 if ((SystemContext
.SystemContextX64
->ExceptionData
& IA32_PF_EC_ID
) != 0) {
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
);
873 if (IsSmmCommBufferForbiddenAddress (PFAddress
)) {
874 DEBUG ((DEBUG_ERROR
, "Access SMM communication forbidden address (0x%lx)!\n", PFAddress
));
876 DumpModuleInfoByIp ((UINTN
)SystemContext
.SystemContextX64
->Rip
);
882 if (FeaturePcdGet (PcdCpuSmmProfileEnable
)) {
883 SmmProfilePFHandler (
884 SystemContext
.SystemContextX64
->Rip
,
885 SystemContext
.SystemContextX64
->ExceptionData
888 SmiDefaultPFHandler ();
891 ReleaseSpinLock (mPFLock
);
895 This function sets memory attribute for page table.
898 SetPageTableAttributes (
910 BOOLEAN PageTableSplitted
;
912 if (!mCpuSmmStaticPageTable
) {
916 DEBUG ((DEBUG_INFO
, "SetPageTableAttributes\n"));
919 // Disable write protection, because we need mark page table to be write protected.
920 // We need *write* page table memory, to mark itself to be *read only*.
922 AsmWriteCr0 (AsmReadCr0() & ~CR0_WP
);
925 DEBUG ((DEBUG_INFO
, "Start...\n"));
926 PageTableSplitted
= FALSE
;
928 L4PageTable
= (UINT64
*)GetPageTableBase ();
929 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L4PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
930 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
932 for (Index4
= 0; Index4
< SIZE_4KB
/sizeof(UINT64
); Index4
++) {
933 L3PageTable
= (UINT64
*)(UINTN
)(L4PageTable
[Index4
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
934 if (L3PageTable
== NULL
) {
938 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L3PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
939 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
941 for (Index3
= 0; Index3
< SIZE_4KB
/sizeof(UINT64
); Index3
++) {
942 if ((L3PageTable
[Index3
] & IA32_PG_PS
) != 0) {
946 L2PageTable
= (UINT64
*)(UINTN
)(L3PageTable
[Index3
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
947 if (L2PageTable
== NULL
) {
951 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L2PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
952 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
954 for (Index2
= 0; Index2
< SIZE_4KB
/sizeof(UINT64
); Index2
++) {
955 if ((L2PageTable
[Index2
] & IA32_PG_PS
) != 0) {
959 L1PageTable
= (UINT64
*)(UINTN
)(L2PageTable
[Index2
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
960 if (L1PageTable
== NULL
) {
963 SmmSetMemoryAttributesEx ((EFI_PHYSICAL_ADDRESS
)(UINTN
)L1PageTable
, SIZE_4KB
, EFI_MEMORY_RO
, &IsSplitted
);
964 PageTableSplitted
= (PageTableSplitted
|| IsSplitted
);
968 } while (PageTableSplitted
);
971 // Enable write protection, after page table updated.
973 AsmWriteCr0 (AsmReadCr0() | CR0_WP
);