2 Page table management support.
4 Copyright (c) 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.
19 #include <Library/BaseLib.h>
20 #include <Library/CpuLib.h>
21 #include <Library/BaseMemoryLib.h>
22 #include <Library/MemoryAllocationLib.h>
23 #include <Library/DebugLib.h>
24 #include <Library/UefiBootServicesTableLib.h>
25 #include <Library/PeCoffGetEntryPointLib.h>
26 #include <Library/SerialPortLib.h>
27 #include <Library/SynchronizationLib.h>
28 #include <Library/PrintLib.h>
29 #include <Protocol/MpService.h>
30 #include <Protocol/SmmBase2.h>
31 #include <Register/Cpuid.h>
32 #include <Register/Msr.h>
35 #include "CpuPageTable.h"
48 #define IA32_PG_P BIT0
49 #define IA32_PG_RW BIT1
50 #define IA32_PG_U BIT2
51 #define IA32_PG_WT BIT3
52 #define IA32_PG_CD BIT4
53 #define IA32_PG_A BIT5
54 #define IA32_PG_D BIT6
55 #define IA32_PG_PS BIT7
56 #define IA32_PG_PAT_2M BIT12
57 #define IA32_PG_PAT_4K IA32_PG_PS
58 #define IA32_PG_PMNT BIT62
59 #define IA32_PG_NX BIT63
61 #define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P)
63 // Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE
64 // X64 PAE PDPTE does not have such restriction
66 #define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P)
68 #define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS)
70 #define PAGING_4K_MASK 0xFFF
71 #define PAGING_2M_MASK 0x1FFFFF
72 #define PAGING_1G_MASK 0x3FFFFFFF
74 #define PAGING_PAE_INDEX_MASK 0x1FF
76 #define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
77 #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
78 #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
80 #define MAX_PF_ENTRY_COUNT 10
81 #define MAX_DEBUG_MESSAGE_LENGTH 0x100
82 #define IA32_PF_EC_ID BIT4
92 PAGE_ATTRIBUTE Attribute
;
95 } PAGE_ATTRIBUTE_TABLE
;
103 PAGE_ATTRIBUTE_TABLE mPageAttributeTable
[] = {
104 {Page4K
, SIZE_4KB
, PAGING_4K_ADDRESS_MASK_64
},
105 {Page2M
, SIZE_2MB
, PAGING_2M_ADDRESS_MASK_64
},
106 {Page1G
, SIZE_1GB
, PAGING_1G_ADDRESS_MASK_64
},
109 PAGE_TABLE_POOL
*mPageTablePool
= NULL
;
110 PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext
;
111 EFI_SMM_BASE2_PROTOCOL
*mSmmBase2
= NULL
;
114 // Record the page fault exception count for one instruction execution.
116 UINTN
*mPFEntryCount
;
117 UINT64
*(*mLastPFEntryPointer
)[MAX_PF_ENTRY_COUNT
];
120 Check if current execution environment is in SMM mode or not, via
121 EFI_SMM_BASE2_PROTOCOL.
123 This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib
124 supports to free memory outside SMRAM. The library will call gBS->FreePool() or
125 gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change
126 memory paging attributes during free operation, if some memory related features
127 are enabled (like Heap Guard).
129 This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This
130 will cause incorrect result because SMM mode always loads its own page tables,
131 which are usually different from DXE. This function can be used to detect such
132 situation and help to avoid further misoperations.
134 @retval TRUE In SMM mode.
135 @retval FALSE Not in SMM mode.
145 if (mSmmBase2
== NULL
) {
146 gBS
->LocateProtocol (&gEfiSmmBase2ProtocolGuid
, NULL
, (VOID
**)&mSmmBase2
);
149 if (mSmmBase2
!= NULL
) {
150 mSmmBase2
->InSmm (mSmmBase2
, &InSmm
);
154 // mSmmBase2->InSmm() can only detect if the caller is running in SMRAM
155 // or from SMM driver. It cannot tell if the caller is running in SMM mode.
156 // Check page table base address to guarantee that because SMM mode willl
157 // load its own page table.
160 mPagingContext
.ContextData
.X64
.PageTableBase
!= (UINT64
)AsmReadCr3());
164 Return current paging context.
166 @param[in,out] PagingContext The paging context.
169 GetCurrentPagingContext (
170 IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
174 CPUID_EXTENDED_CPU_SIG_EDX RegEdx
;
175 MSR_IA32_EFER_REGISTER MsrEfer
;
178 // Don't retrieve current paging context from processor if in SMM mode.
181 ZeroMem (&mPagingContext
, sizeof(mPagingContext
));
182 if (sizeof(UINTN
) == sizeof(UINT64
)) {
183 mPagingContext
.MachineType
= IMAGE_FILE_MACHINE_X64
;
185 mPagingContext
.MachineType
= IMAGE_FILE_MACHINE_I386
;
187 if ((AsmReadCr0 () & CR0_PG
) != 0) {
188 mPagingContext
.ContextData
.X64
.PageTableBase
= (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
);
190 mPagingContext
.ContextData
.X64
.PageTableBase
= 0;
193 if ((AsmReadCr4 () & CR4_PSE
) != 0) {
194 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE
;
196 if ((AsmReadCr4 () & CR4_PAE
) != 0) {
197 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
;
199 if ((AsmReadCr0 () & CR0_WP
) != 0) {
200 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE
;
203 AsmCpuid (CPUID_EXTENDED_FUNCTION
, &RegEax
, NULL
, NULL
, NULL
);
204 if (RegEax
>= CPUID_EXTENDED_CPU_SIG
) {
205 AsmCpuid (CPUID_EXTENDED_CPU_SIG
, NULL
, NULL
, NULL
, &RegEdx
.Uint32
);
207 if (RegEdx
.Bits
.NX
!= 0) {
209 MsrEfer
.Uint64
= AsmReadMsr64(MSR_CORE_IA32_EFER
);
210 if (MsrEfer
.Bits
.NXE
!= 0) {
212 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED
;
216 if (RegEdx
.Bits
.Page1GB
!= 0) {
217 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT
;
223 // This can avoid getting SMM paging context if in SMM mode. We cannot assume
224 // SMM mode shares the same paging context as DXE.
226 CopyMem (PagingContext
, &mPagingContext
, sizeof (mPagingContext
));
230 Return length according to page attributes.
232 @param[in] PageAttributes The page attribute of the page entry.
234 @return The length of page entry.
237 PageAttributeToLength (
238 IN PAGE_ATTRIBUTE PageAttribute
242 for (Index
= 0; Index
< sizeof(mPageAttributeTable
)/sizeof(mPageAttributeTable
[0]); Index
++) {
243 if (PageAttribute
== mPageAttributeTable
[Index
].Attribute
) {
244 return (UINTN
)mPageAttributeTable
[Index
].Length
;
251 Return address mask according to page attributes.
253 @param[in] PageAttributes The page attribute of the page entry.
255 @return The address mask of page entry.
258 PageAttributeToMask (
259 IN PAGE_ATTRIBUTE PageAttribute
263 for (Index
= 0; Index
< sizeof(mPageAttributeTable
)/sizeof(mPageAttributeTable
[0]); Index
++) {
264 if (PageAttribute
== mPageAttributeTable
[Index
].Attribute
) {
265 return (UINTN
)mPageAttributeTable
[Index
].AddressMask
;
272 Return page table entry to match the address.
274 @param[in] PagingContext The paging context.
275 @param[in] Address The address to be checked.
276 @param[out] PageAttributes The page attribute of the page entry.
278 @return The page entry.
282 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
,
283 IN PHYSICAL_ADDRESS Address
,
284 OUT PAGE_ATTRIBUTE
*PageAttribute
295 UINT64 AddressEncMask
;
297 ASSERT (PagingContext
!= NULL
);
299 Index4
= ((UINTN
)RShiftU64 (Address
, 39)) & PAGING_PAE_INDEX_MASK
;
300 Index3
= ((UINTN
)Address
>> 30) & PAGING_PAE_INDEX_MASK
;
301 Index2
= ((UINTN
)Address
>> 21) & PAGING_PAE_INDEX_MASK
;
302 Index1
= ((UINTN
)Address
>> 12) & PAGING_PAE_INDEX_MASK
;
304 // Make sure AddressEncMask is contained to smallest supported address field.
306 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
308 if (PagingContext
->MachineType
== IMAGE_FILE_MACHINE_X64
) {
309 L4PageTable
= (UINT64
*)(UINTN
)PagingContext
->ContextData
.X64
.PageTableBase
;
310 if (L4PageTable
[Index4
] == 0) {
311 *PageAttribute
= PageNone
;
315 L3PageTable
= (UINT64
*)(UINTN
)(L4PageTable
[Index4
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
317 ASSERT((PagingContext
->ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
) != 0);
318 L3PageTable
= (UINT64
*)(UINTN
)PagingContext
->ContextData
.Ia32
.PageTableBase
;
320 if (L3PageTable
[Index3
] == 0) {
321 *PageAttribute
= PageNone
;
324 if ((L3PageTable
[Index3
] & IA32_PG_PS
) != 0) {
326 *PageAttribute
= Page1G
;
327 return &L3PageTable
[Index3
];
330 L2PageTable
= (UINT64
*)(UINTN
)(L3PageTable
[Index3
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
331 if (L2PageTable
[Index2
] == 0) {
332 *PageAttribute
= PageNone
;
335 if ((L2PageTable
[Index2
] & IA32_PG_PS
) != 0) {
337 *PageAttribute
= Page2M
;
338 return &L2PageTable
[Index2
];
342 L1PageTable
= (UINT64
*)(UINTN
)(L2PageTable
[Index2
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
343 if ((L1PageTable
[Index1
] == 0) && (Address
!= 0)) {
344 *PageAttribute
= PageNone
;
347 *PageAttribute
= Page4K
;
348 return &L1PageTable
[Index1
];
352 Return memory attributes of page entry.
354 @param[in] PageEntry The page entry.
356 @return Memory attributes of page entry.
359 GetAttributesFromPageEntry (
365 if ((*PageEntry
& IA32_PG_P
) == 0) {
366 Attributes
|= EFI_MEMORY_RP
;
368 if ((*PageEntry
& IA32_PG_RW
) == 0) {
369 Attributes
|= EFI_MEMORY_RO
;
371 if ((*PageEntry
& IA32_PG_NX
) != 0) {
372 Attributes
|= EFI_MEMORY_XP
;
378 Modify memory attributes of page entry.
380 @param[in] PagingContext The paging context.
381 @param[in] PageEntry The page entry.
382 @param[in] Attributes The bit mask of attributes to modify for the memory region.
383 @param[in] PageAction The page action.
384 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
387 ConvertPageEntryAttribute (
388 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
,
389 IN UINT64
*PageEntry
,
390 IN UINT64 Attributes
,
391 IN PAGE_ACTION PageAction
,
392 OUT BOOLEAN
*IsModified
395 UINT64 CurrentPageEntry
;
398 CurrentPageEntry
= *PageEntry
;
399 NewPageEntry
= CurrentPageEntry
;
400 if ((Attributes
& EFI_MEMORY_RP
) != 0) {
401 switch (PageAction
) {
402 case PageActionAssign
:
404 NewPageEntry
&= ~(UINT64
)IA32_PG_P
;
406 case PageActionClear
:
407 NewPageEntry
|= IA32_PG_P
;
411 switch (PageAction
) {
412 case PageActionAssign
:
413 NewPageEntry
|= IA32_PG_P
;
416 case PageActionClear
:
420 if ((Attributes
& EFI_MEMORY_RO
) != 0) {
421 switch (PageAction
) {
422 case PageActionAssign
:
424 NewPageEntry
&= ~(UINT64
)IA32_PG_RW
;
426 case PageActionClear
:
427 NewPageEntry
|= IA32_PG_RW
;
431 switch (PageAction
) {
432 case PageActionAssign
:
433 NewPageEntry
|= IA32_PG_RW
;
436 case PageActionClear
:
440 if ((PagingContext
->ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED
) != 0) {
441 if ((Attributes
& EFI_MEMORY_XP
) != 0) {
442 switch (PageAction
) {
443 case PageActionAssign
:
445 NewPageEntry
|= IA32_PG_NX
;
447 case PageActionClear
:
448 NewPageEntry
&= ~IA32_PG_NX
;
452 switch (PageAction
) {
453 case PageActionAssign
:
454 NewPageEntry
&= ~IA32_PG_NX
;
457 case PageActionClear
:
462 *PageEntry
= NewPageEntry
;
463 if (CurrentPageEntry
!= NewPageEntry
) {
465 DEBUG ((DEBUG_VERBOSE
, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry
));
466 DEBUG ((DEBUG_VERBOSE
, "->0x%lx\n", NewPageEntry
));
473 This function returns if there is need to split page entry.
475 @param[in] BaseAddress The base address to be checked.
476 @param[in] Length The length to be checked.
477 @param[in] PageEntry The page entry to be checked.
478 @param[in] PageAttribute The page attribute of the page entry.
480 @retval SplitAttributes on if there is need to split page entry.
484 IN PHYSICAL_ADDRESS BaseAddress
,
486 IN UINT64
*PageEntry
,
487 IN PAGE_ATTRIBUTE PageAttribute
490 UINT64 PageEntryLength
;
492 PageEntryLength
= PageAttributeToLength (PageAttribute
);
494 if (((BaseAddress
& (PageEntryLength
- 1)) == 0) && (Length
>= PageEntryLength
)) {
498 if (((BaseAddress
& PAGING_2M_MASK
) != 0) || (Length
< SIZE_2MB
)) {
506 This function splits one page entry to small page entries.
508 @param[in] PageEntry The page entry to be splitted.
509 @param[in] PageAttribute The page attribute of the page entry.
510 @param[in] SplitAttribute How to split the page entry.
511 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
513 @retval RETURN_SUCCESS The page entry is splitted.
514 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
515 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
519 IN UINT64
*PageEntry
,
520 IN PAGE_ATTRIBUTE PageAttribute
,
521 IN PAGE_ATTRIBUTE SplitAttribute
,
522 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc
526 UINT64
*NewPageEntry
;
528 UINT64 AddressEncMask
;
530 ASSERT (PageAttribute
== Page2M
|| PageAttribute
== Page1G
);
532 ASSERT (AllocatePagesFunc
!= NULL
);
534 // Make sure AddressEncMask is contained to smallest supported address field.
536 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
538 if (PageAttribute
== Page2M
) {
542 ASSERT (SplitAttribute
== Page4K
);
543 if (SplitAttribute
== Page4K
) {
544 NewPageEntry
= AllocatePagesFunc (1);
545 DEBUG ((DEBUG_VERBOSE
, "Split - 0x%x\n", NewPageEntry
));
546 if (NewPageEntry
== NULL
) {
547 return RETURN_OUT_OF_RESOURCES
;
549 BaseAddress
= *PageEntry
& ~AddressEncMask
& PAGING_2M_ADDRESS_MASK_64
;
550 for (Index
= 0; Index
< SIZE_4KB
/ sizeof(UINT64
); Index
++) {
551 NewPageEntry
[Index
] = (BaseAddress
+ SIZE_4KB
* Index
) | AddressEncMask
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
553 (*PageEntry
) = (UINT64
)(UINTN
)NewPageEntry
| AddressEncMask
| ((*PageEntry
) & PAGE_ATTRIBUTE_BITS
);
554 return RETURN_SUCCESS
;
556 return RETURN_UNSUPPORTED
;
558 } else if (PageAttribute
== Page1G
) {
561 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
563 ASSERT (SplitAttribute
== Page2M
|| SplitAttribute
== Page4K
);
564 if ((SplitAttribute
== Page2M
|| SplitAttribute
== Page4K
)) {
565 NewPageEntry
= AllocatePagesFunc (1);
566 DEBUG ((DEBUG_VERBOSE
, "Split - 0x%x\n", NewPageEntry
));
567 if (NewPageEntry
== NULL
) {
568 return RETURN_OUT_OF_RESOURCES
;
570 BaseAddress
= *PageEntry
& ~AddressEncMask
& PAGING_1G_ADDRESS_MASK_64
;
571 for (Index
= 0; Index
< SIZE_4KB
/ sizeof(UINT64
); Index
++) {
572 NewPageEntry
[Index
] = (BaseAddress
+ SIZE_2MB
* Index
) | AddressEncMask
| IA32_PG_PS
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
574 (*PageEntry
) = (UINT64
)(UINTN
)NewPageEntry
| AddressEncMask
| ((*PageEntry
) & PAGE_ATTRIBUTE_BITS
);
575 return RETURN_SUCCESS
;
577 return RETURN_UNSUPPORTED
;
580 return RETURN_UNSUPPORTED
;
585 Check the WP status in CR0 register. This bit is used to lock or unlock write
586 access to pages marked as read-only.
588 @retval TRUE Write protection is enabled.
589 @retval FALSE Write protection is disabled.
592 IsReadOnlyPageWriteProtected (
597 // To avoid unforseen consequences, don't touch paging settings in SMM mode
601 return ((AsmReadCr0 () & CR0_WP
) != 0);
607 Disable Write Protect on pages marked as read-only.
610 DisableReadOnlyPageWriteProtect (
615 // To avoid unforseen consequences, don't touch paging settings in SMM mode
619 AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP
);
624 Enable Write Protect on pages marked as read-only.
627 EnableReadOnlyPageWriteProtect (
632 // To avoid unforseen consequences, don't touch paging settings in SMM mode
636 AsmWriteCr0 (AsmReadCr0 () | CR0_WP
);
641 This function modifies the page attributes for the memory region specified by BaseAddress and
642 Length from their current attributes to the attributes specified by Attributes.
644 Caller should make sure BaseAddress and Length is at page boundary.
646 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.
647 @param[in] BaseAddress The physical address that is the start address of a memory region.
648 @param[in] Length The size in bytes of the memory region.
649 @param[in] Attributes The bit mask of attributes to modify for the memory region.
650 @param[in] PageAction The page action.
651 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
652 NULL mean page split is unsupported.
653 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
654 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
656 @retval RETURN_SUCCESS The attributes were modified for the memory region.
657 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
658 BaseAddress and Length cannot be modified.
659 @retval RETURN_INVALID_PARAMETER Length is zero.
660 Attributes specified an illegal combination of attributes that
661 cannot be set together.
662 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
663 the memory resource range.
664 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
665 resource range specified by BaseAddress and Length.
666 The bit mask of attributes is not support for the memory resource
667 range specified by BaseAddress and Length.
670 ConvertMemoryPageAttributes (
671 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext OPTIONAL
,
672 IN PHYSICAL_ADDRESS BaseAddress
,
674 IN UINT64 Attributes
,
675 IN PAGE_ACTION PageAction
,
676 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL
,
677 OUT BOOLEAN
*IsSplitted
, OPTIONAL
678 OUT BOOLEAN
*IsModified OPTIONAL
681 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext
;
683 PAGE_ATTRIBUTE PageAttribute
;
684 UINTN PageEntryLength
;
685 PAGE_ATTRIBUTE SplitAttribute
;
686 RETURN_STATUS Status
;
687 BOOLEAN IsEntryModified
;
690 if ((BaseAddress
& (SIZE_4KB
- 1)) != 0) {
691 DEBUG ((DEBUG_ERROR
, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress
));
692 return EFI_UNSUPPORTED
;
694 if ((Length
& (SIZE_4KB
- 1)) != 0) {
695 DEBUG ((DEBUG_ERROR
, "Length(0x%lx) is not aligned!\n", Length
));
696 return EFI_UNSUPPORTED
;
699 DEBUG ((DEBUG_ERROR
, "Length is 0!\n"));
700 return RETURN_INVALID_PARAMETER
;
703 if ((Attributes
& ~(EFI_MEMORY_RP
| EFI_MEMORY_RO
| EFI_MEMORY_XP
)) != 0) {
704 DEBUG ((DEBUG_ERROR
, "Attributes(0x%lx) has unsupported bit\n", Attributes
));
705 return EFI_UNSUPPORTED
;
708 if (PagingContext
== NULL
) {
709 GetCurrentPagingContext (&CurrentPagingContext
);
711 CopyMem (&CurrentPagingContext
, PagingContext
, sizeof(CurrentPagingContext
));
713 switch(CurrentPagingContext
.MachineType
) {
714 case IMAGE_FILE_MACHINE_I386
:
715 if (CurrentPagingContext
.ContextData
.Ia32
.PageTableBase
== 0) {
716 if (Attributes
== 0) {
719 DEBUG ((DEBUG_ERROR
, "PageTable is 0!\n"));
720 return EFI_UNSUPPORTED
;
723 if ((CurrentPagingContext
.ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
) == 0) {
724 DEBUG ((DEBUG_ERROR
, "Non-PAE Paging!\n"));
725 return EFI_UNSUPPORTED
;
727 if ((BaseAddress
+ Length
) > BASE_4GB
) {
728 DEBUG ((DEBUG_ERROR
, "Beyond 4GB memory in 32-bit mode!\n"));
729 return EFI_UNSUPPORTED
;
732 case IMAGE_FILE_MACHINE_X64
:
733 ASSERT (CurrentPagingContext
.ContextData
.X64
.PageTableBase
!= 0);
737 return EFI_UNSUPPORTED
;
741 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
743 if (IsSplitted
!= NULL
) {
746 if (IsModified
!= NULL
) {
749 if (AllocatePagesFunc
== NULL
) {
750 AllocatePagesFunc
= AllocatePageTableMemory
;
754 // Make sure that the page table is changeable.
756 IsWpEnabled
= IsReadOnlyPageWriteProtected ();
758 DisableReadOnlyPageWriteProtect ();
762 // Below logic is to check 2M/4K page to make sure we donot waist memory.
764 Status
= EFI_SUCCESS
;
765 while (Length
!= 0) {
766 PageEntry
= GetPageTableEntry (&CurrentPagingContext
, BaseAddress
, &PageAttribute
);
767 if (PageEntry
== NULL
) {
768 Status
= RETURN_UNSUPPORTED
;
771 PageEntryLength
= PageAttributeToLength (PageAttribute
);
772 SplitAttribute
= NeedSplitPage (BaseAddress
, Length
, PageEntry
, PageAttribute
);
773 if (SplitAttribute
== PageNone
) {
774 ConvertPageEntryAttribute (&CurrentPagingContext
, PageEntry
, Attributes
, PageAction
, &IsEntryModified
);
775 if (IsEntryModified
) {
776 if (IsModified
!= NULL
) {
781 // Convert success, move to next
783 BaseAddress
+= PageEntryLength
;
784 Length
-= PageEntryLength
;
786 if (AllocatePagesFunc
== NULL
) {
787 Status
= RETURN_UNSUPPORTED
;
790 Status
= SplitPage (PageEntry
, PageAttribute
, SplitAttribute
, AllocatePagesFunc
);
791 if (RETURN_ERROR (Status
)) {
792 Status
= RETURN_UNSUPPORTED
;
795 if (IsSplitted
!= NULL
) {
798 if (IsModified
!= NULL
) {
802 // Just split current page
803 // Convert success in next around
810 // Restore page table write protection, if any.
813 EnableReadOnlyPageWriteProtect ();
819 This function assigns the page attributes for the memory region specified by BaseAddress and
820 Length from their current attributes to the attributes specified by Attributes.
822 Caller should make sure BaseAddress and Length is at page boundary.
824 Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
826 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.
827 @param[in] BaseAddress The physical address that is the start address of a memory region.
828 @param[in] Length The size in bytes of the memory region.
829 @param[in] Attributes The bit mask of attributes to set for the memory region.
830 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
831 NULL mean page split is unsupported.
833 @retval RETURN_SUCCESS The attributes were cleared for the memory region.
834 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
835 BaseAddress and Length cannot be modified.
836 @retval RETURN_INVALID_PARAMETER Length is zero.
837 Attributes specified an illegal combination of attributes that
838 cannot be set together.
839 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
840 the memory resource range.
841 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
842 resource range specified by BaseAddress and Length.
843 The bit mask of attributes is not support for the memory resource
844 range specified by BaseAddress and Length.
848 AssignMemoryPageAttributes (
849 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext OPTIONAL
,
850 IN PHYSICAL_ADDRESS BaseAddress
,
852 IN UINT64 Attributes
,
853 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL
856 RETURN_STATUS Status
;
860 // DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes));
861 Status
= ConvertMemoryPageAttributes (PagingContext
, BaseAddress
, Length
, Attributes
, PageActionAssign
, AllocatePagesFunc
, &IsSplitted
, &IsModified
);
862 if (!EFI_ERROR(Status
)) {
863 if ((PagingContext
== NULL
) && IsModified
) {
865 // Flush TLB as last step.
867 // Note: Since APs will always init CR3 register in HLT loop mode or do
868 // TLB flush in MWAIT loop mode, there's no need to flush TLB for them
879 Check if Execute Disable feature is enabled or not.
882 IsExecuteDisableEnabled (
886 MSR_CORE_IA32_EFER_REGISTER MsrEfer
;
888 MsrEfer
.Uint64
= AsmReadMsr64 (MSR_IA32_EFER
);
889 return (MsrEfer
.Bits
.NXE
== 1);
893 Update GCD memory space attributes according to current page table setup.
896 RefreshGcdMemoryAttributesFromPaging (
901 UINTN NumberOfDescriptors
;
902 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
903 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext
;
904 PAGE_ATTRIBUTE PageAttribute
;
907 UINT64 MemorySpaceLength
;
910 UINT64 PageStartAddress
;
913 UINT64 NewAttributes
;
917 // Assuming that memory space map returned is sorted already; otherwise sort
918 // them in the order of lowest address to highest address.
920 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
921 ASSERT_EFI_ERROR (Status
);
923 GetCurrentPagingContext (&PagingContext
);
930 if (IsExecuteDisableEnabled ()) {
931 Capabilities
= EFI_MEMORY_RO
| EFI_MEMORY_RP
| EFI_MEMORY_XP
;
933 Capabilities
= EFI_MEMORY_RO
| EFI_MEMORY_RP
;
936 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
937 if (MemorySpaceMap
[Index
].GcdMemoryType
== EfiGcdMemoryTypeNonExistent
) {
942 // Sync the actual paging related capabilities back to GCD service first.
943 // As a side effect (good one), this can also help to avoid unnecessary
944 // memory map entries due to the different capabilities of the same type
945 // memory, such as multiple RT_CODE and RT_DATA entries in memory map,
946 // which could cause boot failure of some old Linux distro (before v4.3).
948 Status
= gDS
->SetMemorySpaceCapabilities (
949 MemorySpaceMap
[Index
].BaseAddress
,
950 MemorySpaceMap
[Index
].Length
,
951 MemorySpaceMap
[Index
].Capabilities
| Capabilities
953 if (EFI_ERROR (Status
)) {
955 // If we cannot udpate the capabilities, we cannot update its
956 // attributes either. So just simply skip current block of memory.
960 "Failed to update capability: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",
961 (UINT64
)Index
, MemorySpaceMap
[Index
].BaseAddress
,
962 MemorySpaceMap
[Index
].BaseAddress
+ MemorySpaceMap
[Index
].Length
- 1,
963 MemorySpaceMap
[Index
].Capabilities
,
964 MemorySpaceMap
[Index
].Capabilities
| Capabilities
969 if (MemorySpaceMap
[Index
].BaseAddress
>= (BaseAddress
+ PageLength
)) {
971 // Current memory space starts at a new page. Resetting PageLength will
972 // trigger a retrieval of page attributes at new address.
977 // In case current memory space is not adjacent to last one
979 PageLength
-= (MemorySpaceMap
[Index
].BaseAddress
- BaseAddress
);
983 // Sync actual page attributes to GCD
985 BaseAddress
= MemorySpaceMap
[Index
].BaseAddress
;
986 MemorySpaceLength
= MemorySpaceMap
[Index
].Length
;
987 while (MemorySpaceLength
> 0) {
988 if (PageLength
== 0) {
989 PageEntry
= GetPageTableEntry (&PagingContext
, BaseAddress
, &PageAttribute
);
990 if (PageEntry
== NULL
) {
995 // Note current memory space might start in the middle of a page
997 PageStartAddress
= (*PageEntry
) & (UINT64
)PageAttributeToMask(PageAttribute
);
998 PageLength
= PageAttributeToLength (PageAttribute
) - (BaseAddress
- PageStartAddress
);
999 Attributes
= GetAttributesFromPageEntry (PageEntry
);
1002 Length
= MIN (PageLength
, MemorySpaceLength
);
1003 if (Attributes
!= (MemorySpaceMap
[Index
].Attributes
&
1004 EFI_MEMORY_PAGETYPE_MASK
)) {
1005 NewAttributes
= (MemorySpaceMap
[Index
].Attributes
&
1006 ~EFI_MEMORY_PAGETYPE_MASK
) | Attributes
;
1007 Status
= gDS
->SetMemorySpaceAttributes (
1012 ASSERT_EFI_ERROR (Status
);
1015 "Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",
1016 (UINT64
)Index
, BaseAddress
, BaseAddress
+ Length
- 1,
1017 MemorySpaceMap
[Index
].Attributes
,
1022 PageLength
-= Length
;
1023 MemorySpaceLength
-= Length
;
1024 BaseAddress
+= Length
;
1028 FreePool (MemorySpaceMap
);
1032 Initialize a buffer pool for page table use only.
1034 To reduce the potential split operation on page table, the pages reserved for
1035 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
1036 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
1037 initialized with number of pages greater than or equal to the given PoolPages.
1039 Once the pages in the pool are used up, this method should be called again to
1040 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't happen
1043 @param[in] PoolPages The least page number of the pool to be created.
1045 @retval TRUE The pool is initialized successfully.
1046 @retval FALSE The memory is out of resource.
1049 InitializePageTablePool (
1057 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
1060 PoolPages
+= 1; // Add one page for header.
1061 PoolPages
= ((PoolPages
- 1) / PAGE_TABLE_POOL_UNIT_PAGES
+ 1) *
1062 PAGE_TABLE_POOL_UNIT_PAGES
;
1063 Buffer
= AllocateAlignedPages (PoolPages
, PAGE_TABLE_POOL_ALIGNMENT
);
1064 if (Buffer
== NULL
) {
1065 DEBUG ((DEBUG_ERROR
, "ERROR: Out of aligned pages\r\n"));
1070 // Link all pools into a list for easier track later.
1072 if (mPageTablePool
== NULL
) {
1073 mPageTablePool
= Buffer
;
1074 mPageTablePool
->NextPool
= mPageTablePool
;
1076 ((PAGE_TABLE_POOL
*)Buffer
)->NextPool
= mPageTablePool
->NextPool
;
1077 mPageTablePool
->NextPool
= Buffer
;
1078 mPageTablePool
= Buffer
;
1082 // Reserve one page for pool header.
1084 mPageTablePool
->FreePages
= PoolPages
- 1;
1085 mPageTablePool
->Offset
= EFI_PAGES_TO_SIZE (1);
1088 // Mark the whole pool pages as read-only.
1090 ConvertMemoryPageAttributes (
1092 (PHYSICAL_ADDRESS
)(UINTN
)Buffer
,
1093 EFI_PAGES_TO_SIZE (PoolPages
),
1096 AllocatePageTableMemory
,
1100 ASSERT (IsModified
== TRUE
);
1106 This API provides a way to allocate memory for page table.
1108 This API can be called more than once to allocate memory for page tables.
1110 Allocates the number of 4KB pages and returns a pointer to the allocated
1111 buffer. The buffer returned is aligned on a 4KB boundary.
1113 If Pages is 0, then NULL is returned.
1114 If there is not enough memory remaining to satisfy the request, then NULL is
1117 @param Pages The number of 4 KB pages to allocate.
1119 @return A pointer to the allocated buffer or NULL if allocation fails.
1124 AllocatePageTableMemory (
1135 // Renew the pool if necessary.
1137 if (mPageTablePool
== NULL
||
1138 Pages
> mPageTablePool
->FreePages
) {
1139 if (!InitializePageTablePool (Pages
)) {
1144 Buffer
= (UINT8
*)mPageTablePool
+ mPageTablePool
->Offset
;
1146 mPageTablePool
->Offset
+= EFI_PAGES_TO_SIZE (Pages
);
1147 mPageTablePool
->FreePages
-= Pages
;
1153 Special handler for #DB exception, which will restore the page attributes
1154 (not-present). It should work with #PF handler which will set pages to
1157 @param ExceptionType Exception type.
1158 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
1163 DebugExceptionHandler (
1164 IN EFI_EXCEPTION_TYPE ExceptionType
,
1165 IN EFI_SYSTEM_CONTEXT SystemContext
1170 BOOLEAN IsWpEnabled
;
1172 MpInitLibWhoAmI (&CpuIndex
);
1175 // Clear last PF entries
1177 IsWpEnabled
= IsReadOnlyPageWriteProtected ();
1179 DisableReadOnlyPageWriteProtect ();
1182 for (PFEntry
= 0; PFEntry
< mPFEntryCount
[CpuIndex
]; PFEntry
++) {
1183 if (mLastPFEntryPointer
[CpuIndex
][PFEntry
] != NULL
) {
1184 *mLastPFEntryPointer
[CpuIndex
][PFEntry
] &= ~(UINT64
)IA32_PG_P
;
1189 EnableReadOnlyPageWriteProtect ();
1193 // Reset page fault exception count for next page fault.
1195 mPFEntryCount
[CpuIndex
] = 0;
1203 // Clear TF in EFLAGS
1205 if (mPagingContext
.MachineType
== IMAGE_FILE_MACHINE_I386
) {
1206 SystemContext
.SystemContextIa32
->Eflags
&= (UINT32
)~BIT8
;
1208 SystemContext
.SystemContextX64
->Rflags
&= (UINT64
)~BIT8
;
1213 Special handler for #PF exception, which will set the pages which caused
1214 #PF to be 'present'. The attribute of those pages should be restored in
1215 the subsequent #DB handler.
1217 @param ExceptionType Exception type.
1218 @param SystemContext Pointer to EFI_SYSTEM_CONTEXT.
1223 PageFaultExceptionHandler (
1224 IN EFI_EXCEPTION_TYPE ExceptionType
,
1225 IN EFI_SYSTEM_CONTEXT SystemContext
1230 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext
;
1231 PAGE_ATTRIBUTE PageAttribute
;
1237 BOOLEAN NonStopMode
;
1239 PFAddress
= AsmReadCr2 () & ~EFI_PAGE_MASK
;
1240 if (PFAddress
< BASE_4KB
) {
1241 NonStopMode
= NULL_DETECTION_NONSTOP_MODE
? TRUE
: FALSE
;
1243 NonStopMode
= HEAP_GUARD_NONSTOP_MODE
? TRUE
: FALSE
;
1247 MpInitLibWhoAmI (&CpuIndex
);
1248 GetCurrentPagingContext (&PagingContext
);
1250 // Memory operation cross page boundary, like "rep mov" instruction, will
1251 // cause infinite loop between this and Debug Trap handler. We have to make
1252 // sure that current page and the page followed are both in PRESENT state.
1255 while (PageNumber
> 0) {
1256 PageEntry
= GetPageTableEntry (&PagingContext
, PFAddress
, &PageAttribute
);
1257 ASSERT(PageEntry
!= NULL
);
1259 if (PageEntry
!= NULL
) {
1260 Attributes
= GetAttributesFromPageEntry (PageEntry
);
1261 if ((Attributes
& EFI_MEMORY_RP
) != 0) {
1262 Attributes
&= ~EFI_MEMORY_RP
;
1263 Status
= AssignMemoryPageAttributes (&PagingContext
, PFAddress
,
1264 EFI_PAGE_SIZE
, Attributes
, NULL
);
1265 if (!EFI_ERROR(Status
)) {
1266 Index
= mPFEntryCount
[CpuIndex
];
1268 // Re-retrieve page entry because above calling might update page
1269 // table due to table split.
1271 PageEntry
= GetPageTableEntry (&PagingContext
, PFAddress
, &PageAttribute
);
1272 mLastPFEntryPointer
[CpuIndex
][Index
++] = PageEntry
;
1273 mPFEntryCount
[CpuIndex
] = Index
;
1278 PFAddress
+= EFI_PAGE_SIZE
;
1284 // Initialize the serial port before dumping.
1286 SerialPortInitialize ();
1288 // Display ExceptionType, CPU information and Image information
1290 DumpCpuContext (ExceptionType
, SystemContext
);
1297 Initialize the Page Table lib.
1300 InitializePageTableLib (
1304 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext
;
1306 GetCurrentPagingContext (&CurrentPagingContext
);
1309 // Reserve memory of page tables for future uses, if paging is enabled.
1311 if (CurrentPagingContext
.ContextData
.X64
.PageTableBase
!= 0 &&
1312 (CurrentPagingContext
.ContextData
.Ia32
.Attributes
&
1313 PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
) != 0) {
1314 DisableReadOnlyPageWriteProtect ();
1315 InitializePageTablePool (1);
1316 EnableReadOnlyPageWriteProtect ();
1319 if (HEAP_GUARD_NONSTOP_MODE
|| NULL_DETECTION_NONSTOP_MODE
) {
1320 mPFEntryCount
= (UINTN
*)AllocateZeroPool (sizeof (UINTN
) * mNumberOfProcessors
);
1321 ASSERT (mPFEntryCount
!= NULL
);
1323 mLastPFEntryPointer
= (UINT64
*(*)[MAX_PF_ENTRY_COUNT
])
1324 AllocateZeroPool (sizeof (mLastPFEntryPointer
[0]) * mNumberOfProcessors
);
1325 ASSERT (mLastPFEntryPointer
!= NULL
);
1328 DEBUG ((DEBUG_INFO
, "CurrentPagingContext:\n", CurrentPagingContext
.MachineType
));
1329 DEBUG ((DEBUG_INFO
, " MachineType - 0x%x\n", CurrentPagingContext
.MachineType
));
1330 DEBUG ((DEBUG_INFO
, " PageTableBase - 0x%x\n", CurrentPagingContext
.ContextData
.X64
.PageTableBase
));
1331 DEBUG ((DEBUG_INFO
, " Attributes - 0x%x\n", CurrentPagingContext
.ContextData
.X64
.Attributes
));