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 <Protocol/MpService.h>
26 #include <Protocol/SmmBase2.h>
29 #include "CpuPageTable.h"
34 #define IA32_PG_P BIT0
35 #define IA32_PG_RW BIT1
36 #define IA32_PG_U BIT2
37 #define IA32_PG_WT BIT3
38 #define IA32_PG_CD BIT4
39 #define IA32_PG_A BIT5
40 #define IA32_PG_D BIT6
41 #define IA32_PG_PS BIT7
42 #define IA32_PG_PAT_2M BIT12
43 #define IA32_PG_PAT_4K IA32_PG_PS
44 #define IA32_PG_PMNT BIT62
45 #define IA32_PG_NX BIT63
47 #define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P)
49 // Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE
50 // X64 PAE PDPTE does not have such restriction
52 #define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P)
54 #define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS)
56 #define PAGING_4K_MASK 0xFFF
57 #define PAGING_2M_MASK 0x1FFFFF
58 #define PAGING_1G_MASK 0x3FFFFFFF
60 #define PAGING_PAE_INDEX_MASK 0x1FF
62 #define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
63 #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
64 #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
74 PAGE_ATTRIBUTE Attribute
;
77 } PAGE_ATTRIBUTE_TABLE
;
85 PAGE_ATTRIBUTE_TABLE mPageAttributeTable
[] = {
86 {Page4K
, SIZE_4KB
, PAGING_4K_ADDRESS_MASK_64
},
87 {Page2M
, SIZE_2MB
, PAGING_2M_ADDRESS_MASK_64
},
88 {Page1G
, SIZE_1GB
, PAGING_1G_ADDRESS_MASK_64
},
91 PAGE_TABLE_POOL
*mPageTablePool
= NULL
;
92 PAGE_TABLE_LIB_PAGING_CONTEXT mPagingContext
;
93 EFI_SMM_BASE2_PROTOCOL
*mSmmBase2
= NULL
;
96 Check if current execution environment is in SMM mode or not, via
97 EFI_SMM_BASE2_PROTOCOL.
99 This is necessary because of the fact that MdePkg\Library\SmmMemoryAllocationLib
100 supports to free memory outside SMRAM. The library will call gBS->FreePool() or
101 gBS->FreePages() and then SetMemorySpaceAttributes interface in turn to change
102 memory paging attributes during free operation, if some memory related features
103 are enabled (like Heap Guard).
105 This means that SetMemorySpaceAttributes() has chance to run in SMM mode. This
106 will cause incorrect result because SMM mode always loads its own page tables,
107 which are usually different from DXE. This function can be used to detect such
108 situation and help to avoid further misoperations.
110 @retval TRUE In SMM mode.
111 @retval FALSE Not in SMM mode.
121 if (mSmmBase2
== NULL
) {
122 gBS
->LocateProtocol (&gEfiSmmBase2ProtocolGuid
, NULL
, (VOID
**)&mSmmBase2
);
125 if (mSmmBase2
!= NULL
) {
126 mSmmBase2
->InSmm (mSmmBase2
, &InSmm
);
133 Return current paging context.
135 @param[in,out] PagingContext The paging context.
138 GetCurrentPagingContext (
139 IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
146 // Don't retrieve current paging context from processor if in SMM mode.
149 ZeroMem (&mPagingContext
, sizeof(mPagingContext
));
150 if (sizeof(UINTN
) == sizeof(UINT64
)) {
151 mPagingContext
.MachineType
= IMAGE_FILE_MACHINE_X64
;
153 mPagingContext
.MachineType
= IMAGE_FILE_MACHINE_I386
;
155 if ((AsmReadCr0 () & BIT31
) != 0) {
156 mPagingContext
.ContextData
.X64
.PageTableBase
= (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
);
158 mPagingContext
.ContextData
.X64
.PageTableBase
= 0;
161 if ((AsmReadCr4 () & BIT4
) != 0) {
162 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE
;
164 if ((AsmReadCr4 () & BIT5
) != 0) {
165 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
;
167 if ((AsmReadCr0 () & BIT16
) != 0) {
168 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE
;
171 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
172 if (RegEax
> 0x80000000) {
173 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
174 if ((RegEdx
& BIT20
) != 0) {
176 if ((AsmReadMsr64 (0xC0000080) & BIT11
) != 0) {
178 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED
;
181 if ((RegEdx
& BIT26
) != 0) {
182 mPagingContext
.ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT
;
188 // This can avoid getting SMM paging context if in SMM mode. We cannot assume
189 // SMM mode shares the same paging context as DXE.
191 CopyMem (PagingContext
, &mPagingContext
, sizeof (mPagingContext
));
195 Return length according to page attributes.
197 @param[in] PageAttributes The page attribute of the page entry.
199 @return The length of page entry.
202 PageAttributeToLength (
203 IN PAGE_ATTRIBUTE PageAttribute
207 for (Index
= 0; Index
< sizeof(mPageAttributeTable
)/sizeof(mPageAttributeTable
[0]); Index
++) {
208 if (PageAttribute
== mPageAttributeTable
[Index
].Attribute
) {
209 return (UINTN
)mPageAttributeTable
[Index
].Length
;
216 Return address mask according to page attributes.
218 @param[in] PageAttributes The page attribute of the page entry.
220 @return The address mask of page entry.
223 PageAttributeToMask (
224 IN PAGE_ATTRIBUTE PageAttribute
228 for (Index
= 0; Index
< sizeof(mPageAttributeTable
)/sizeof(mPageAttributeTable
[0]); Index
++) {
229 if (PageAttribute
== mPageAttributeTable
[Index
].Attribute
) {
230 return (UINTN
)mPageAttributeTable
[Index
].AddressMask
;
237 Return page table entry to match the address.
239 @param[in] PagingContext The paging context.
240 @param[in] Address The address to be checked.
241 @param[out] PageAttributes The page attribute of the page entry.
243 @return The page entry.
247 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
,
248 IN PHYSICAL_ADDRESS Address
,
249 OUT PAGE_ATTRIBUTE
*PageAttribute
260 UINT64 AddressEncMask
;
262 ASSERT (PagingContext
!= NULL
);
264 Index4
= ((UINTN
)RShiftU64 (Address
, 39)) & PAGING_PAE_INDEX_MASK
;
265 Index3
= ((UINTN
)Address
>> 30) & PAGING_PAE_INDEX_MASK
;
266 Index2
= ((UINTN
)Address
>> 21) & PAGING_PAE_INDEX_MASK
;
267 Index1
= ((UINTN
)Address
>> 12) & PAGING_PAE_INDEX_MASK
;
269 // Make sure AddressEncMask is contained to smallest supported address field.
271 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
273 if (PagingContext
->MachineType
== IMAGE_FILE_MACHINE_X64
) {
274 L4PageTable
= (UINT64
*)(UINTN
)PagingContext
->ContextData
.X64
.PageTableBase
;
275 if (L4PageTable
[Index4
] == 0) {
276 *PageAttribute
= PageNone
;
280 L3PageTable
= (UINT64
*)(UINTN
)(L4PageTable
[Index4
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
282 ASSERT((PagingContext
->ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
) != 0);
283 L3PageTable
= (UINT64
*)(UINTN
)PagingContext
->ContextData
.Ia32
.PageTableBase
;
285 if (L3PageTable
[Index3
] == 0) {
286 *PageAttribute
= PageNone
;
289 if ((L3PageTable
[Index3
] & IA32_PG_PS
) != 0) {
291 *PageAttribute
= Page1G
;
292 return &L3PageTable
[Index3
];
295 L2PageTable
= (UINT64
*)(UINTN
)(L3PageTable
[Index3
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
296 if (L2PageTable
[Index2
] == 0) {
297 *PageAttribute
= PageNone
;
300 if ((L2PageTable
[Index2
] & IA32_PG_PS
) != 0) {
302 *PageAttribute
= Page2M
;
303 return &L2PageTable
[Index2
];
307 L1PageTable
= (UINT64
*)(UINTN
)(L2PageTable
[Index2
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
308 if ((L1PageTable
[Index1
] == 0) && (Address
!= 0)) {
309 *PageAttribute
= PageNone
;
312 *PageAttribute
= Page4K
;
313 return &L1PageTable
[Index1
];
317 Return memory attributes of page entry.
319 @param[in] PageEntry The page entry.
321 @return Memory attributes of page entry.
324 GetAttributesFromPageEntry (
330 if ((*PageEntry
& IA32_PG_P
) == 0) {
331 Attributes
|= EFI_MEMORY_RP
;
333 if ((*PageEntry
& IA32_PG_RW
) == 0) {
334 Attributes
|= EFI_MEMORY_RO
;
336 if ((*PageEntry
& IA32_PG_NX
) != 0) {
337 Attributes
|= EFI_MEMORY_XP
;
343 Modify memory attributes of page entry.
345 @param[in] PagingContext The paging context.
346 @param[in] PageEntry The page entry.
347 @param[in] Attributes The bit mask of attributes to modify for the memory region.
348 @param[in] PageAction The page action.
349 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
352 ConvertPageEntryAttribute (
353 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
,
354 IN UINT64
*PageEntry
,
355 IN UINT64 Attributes
,
356 IN PAGE_ACTION PageAction
,
357 OUT BOOLEAN
*IsModified
360 UINT64 CurrentPageEntry
;
363 CurrentPageEntry
= *PageEntry
;
364 NewPageEntry
= CurrentPageEntry
;
365 if ((Attributes
& EFI_MEMORY_RP
) != 0) {
366 switch (PageAction
) {
367 case PageActionAssign
:
369 NewPageEntry
&= ~(UINT64
)IA32_PG_P
;
371 case PageActionClear
:
372 NewPageEntry
|= IA32_PG_P
;
376 switch (PageAction
) {
377 case PageActionAssign
:
378 NewPageEntry
|= IA32_PG_P
;
381 case PageActionClear
:
385 if ((Attributes
& EFI_MEMORY_RO
) != 0) {
386 switch (PageAction
) {
387 case PageActionAssign
:
389 NewPageEntry
&= ~(UINT64
)IA32_PG_RW
;
391 case PageActionClear
:
392 NewPageEntry
|= IA32_PG_RW
;
396 switch (PageAction
) {
397 case PageActionAssign
:
398 NewPageEntry
|= IA32_PG_RW
;
401 case PageActionClear
:
405 if ((PagingContext
->ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED
) != 0) {
406 if ((Attributes
& EFI_MEMORY_XP
) != 0) {
407 switch (PageAction
) {
408 case PageActionAssign
:
410 NewPageEntry
|= IA32_PG_NX
;
412 case PageActionClear
:
413 NewPageEntry
&= ~IA32_PG_NX
;
417 switch (PageAction
) {
418 case PageActionAssign
:
419 NewPageEntry
&= ~IA32_PG_NX
;
422 case PageActionClear
:
427 *PageEntry
= NewPageEntry
;
428 if (CurrentPageEntry
!= NewPageEntry
) {
430 DEBUG ((DEBUG_VERBOSE
, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry
));
431 DEBUG ((DEBUG_VERBOSE
, "->0x%lx\n", NewPageEntry
));
438 This function returns if there is need to split page entry.
440 @param[in] BaseAddress The base address to be checked.
441 @param[in] Length The length to be checked.
442 @param[in] PageEntry The page entry to be checked.
443 @param[in] PageAttribute The page attribute of the page entry.
445 @retval SplitAttributes on if there is need to split page entry.
449 IN PHYSICAL_ADDRESS BaseAddress
,
451 IN UINT64
*PageEntry
,
452 IN PAGE_ATTRIBUTE PageAttribute
455 UINT64 PageEntryLength
;
457 PageEntryLength
= PageAttributeToLength (PageAttribute
);
459 if (((BaseAddress
& (PageEntryLength
- 1)) == 0) && (Length
>= PageEntryLength
)) {
463 if (((BaseAddress
& PAGING_2M_MASK
) != 0) || (Length
< SIZE_2MB
)) {
471 This function splits one page entry to small page entries.
473 @param[in] PageEntry The page entry to be splitted.
474 @param[in] PageAttribute The page attribute of the page entry.
475 @param[in] SplitAttribute How to split the page entry.
476 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
478 @retval RETURN_SUCCESS The page entry is splitted.
479 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
480 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
484 IN UINT64
*PageEntry
,
485 IN PAGE_ATTRIBUTE PageAttribute
,
486 IN PAGE_ATTRIBUTE SplitAttribute
,
487 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc
491 UINT64
*NewPageEntry
;
493 UINT64 AddressEncMask
;
495 ASSERT (PageAttribute
== Page2M
|| PageAttribute
== Page1G
);
497 ASSERT (AllocatePagesFunc
!= NULL
);
499 // Make sure AddressEncMask is contained to smallest supported address field.
501 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
503 if (PageAttribute
== Page2M
) {
507 ASSERT (SplitAttribute
== Page4K
);
508 if (SplitAttribute
== Page4K
) {
509 NewPageEntry
= AllocatePagesFunc (1);
510 DEBUG ((DEBUG_INFO
, "Split - 0x%x\n", NewPageEntry
));
511 if (NewPageEntry
== NULL
) {
512 return RETURN_OUT_OF_RESOURCES
;
514 BaseAddress
= *PageEntry
& ~AddressEncMask
& PAGING_2M_ADDRESS_MASK_64
;
515 for (Index
= 0; Index
< SIZE_4KB
/ sizeof(UINT64
); Index
++) {
516 NewPageEntry
[Index
] = (BaseAddress
+ SIZE_4KB
* Index
) | AddressEncMask
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
518 (*PageEntry
) = (UINT64
)(UINTN
)NewPageEntry
| AddressEncMask
| ((*PageEntry
) & PAGE_ATTRIBUTE_BITS
);
519 return RETURN_SUCCESS
;
521 return RETURN_UNSUPPORTED
;
523 } else if (PageAttribute
== Page1G
) {
526 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
528 ASSERT (SplitAttribute
== Page2M
|| SplitAttribute
== Page4K
);
529 if ((SplitAttribute
== Page2M
|| SplitAttribute
== Page4K
)) {
530 NewPageEntry
= AllocatePagesFunc (1);
531 DEBUG ((DEBUG_INFO
, "Split - 0x%x\n", NewPageEntry
));
532 if (NewPageEntry
== NULL
) {
533 return RETURN_OUT_OF_RESOURCES
;
535 BaseAddress
= *PageEntry
& ~AddressEncMask
& PAGING_1G_ADDRESS_MASK_64
;
536 for (Index
= 0; Index
< SIZE_4KB
/ sizeof(UINT64
); Index
++) {
537 NewPageEntry
[Index
] = (BaseAddress
+ SIZE_2MB
* Index
) | AddressEncMask
| IA32_PG_PS
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
539 (*PageEntry
) = (UINT64
)(UINTN
)NewPageEntry
| AddressEncMask
| ((*PageEntry
) & PAGE_ATTRIBUTE_BITS
);
540 return RETURN_SUCCESS
;
542 return RETURN_UNSUPPORTED
;
545 return RETURN_UNSUPPORTED
;
550 Check the WP status in CR0 register. This bit is used to lock or unlock write
551 access to pages marked as read-only.
553 @retval TRUE Write protection is enabled.
554 @retval FALSE Write protection is disabled.
557 IsReadOnlyPageWriteProtected (
562 // To avoid unforseen consequences, don't touch paging settings in SMM mode
566 return ((AsmReadCr0 () & BIT16
) != 0);
572 Disable Write Protect on pages marked as read-only.
575 DisableReadOnlyPageWriteProtect (
580 // To avoid unforseen consequences, don't touch paging settings in SMM mode
584 AsmWriteCr0 (AsmReadCr0 () & ~BIT16
);
589 Enable Write Protect on pages marked as read-only.
592 EnableReadOnlyPageWriteProtect (
597 // To avoid unforseen consequences, don't touch paging settings in SMM mode
601 AsmWriteCr0 (AsmReadCr0 () | BIT16
);
606 This function modifies the page attributes for the memory region specified by BaseAddress and
607 Length from their current attributes to the attributes specified by Attributes.
609 Caller should make sure BaseAddress and Length is at page boundary.
611 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.
612 @param[in] BaseAddress The physical address that is the start address of a memory region.
613 @param[in] Length The size in bytes of the memory region.
614 @param[in] Attributes The bit mask of attributes to modify for the memory region.
615 @param[in] PageAction The page action.
616 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
617 NULL mean page split is unsupported.
618 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
619 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
621 @retval RETURN_SUCCESS The attributes were modified for the memory region.
622 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
623 BaseAddress and Length cannot be modified.
624 @retval RETURN_INVALID_PARAMETER Length is zero.
625 Attributes specified an illegal combination of attributes that
626 cannot be set together.
627 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
628 the memory resource range.
629 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
630 resource range specified by BaseAddress and Length.
631 The bit mask of attributes is not support for the memory resource
632 range specified by BaseAddress and Length.
635 ConvertMemoryPageAttributes (
636 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext OPTIONAL
,
637 IN PHYSICAL_ADDRESS BaseAddress
,
639 IN UINT64 Attributes
,
640 IN PAGE_ACTION PageAction
,
641 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL
,
642 OUT BOOLEAN
*IsSplitted
, OPTIONAL
643 OUT BOOLEAN
*IsModified OPTIONAL
646 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext
;
648 PAGE_ATTRIBUTE PageAttribute
;
649 UINTN PageEntryLength
;
650 PAGE_ATTRIBUTE SplitAttribute
;
651 RETURN_STATUS Status
;
652 BOOLEAN IsEntryModified
;
655 if ((BaseAddress
& (SIZE_4KB
- 1)) != 0) {
656 DEBUG ((DEBUG_ERROR
, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress
));
657 return EFI_UNSUPPORTED
;
659 if ((Length
& (SIZE_4KB
- 1)) != 0) {
660 DEBUG ((DEBUG_ERROR
, "Length(0x%lx) is not aligned!\n", Length
));
661 return EFI_UNSUPPORTED
;
664 DEBUG ((DEBUG_ERROR
, "Length is 0!\n"));
665 return RETURN_INVALID_PARAMETER
;
668 if ((Attributes
& ~(EFI_MEMORY_RP
| EFI_MEMORY_RO
| EFI_MEMORY_XP
)) != 0) {
669 DEBUG ((DEBUG_ERROR
, "Attributes(0x%lx) has unsupported bit\n", Attributes
));
670 return EFI_UNSUPPORTED
;
673 if (PagingContext
== NULL
) {
674 GetCurrentPagingContext (&CurrentPagingContext
);
676 CopyMem (&CurrentPagingContext
, PagingContext
, sizeof(CurrentPagingContext
));
678 switch(CurrentPagingContext
.MachineType
) {
679 case IMAGE_FILE_MACHINE_I386
:
680 if (CurrentPagingContext
.ContextData
.Ia32
.PageTableBase
== 0) {
681 if (Attributes
== 0) {
684 DEBUG ((DEBUG_ERROR
, "PageTable is 0!\n"));
685 return EFI_UNSUPPORTED
;
688 if ((CurrentPagingContext
.ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
) == 0) {
689 DEBUG ((DEBUG_ERROR
, "Non-PAE Paging!\n"));
690 return EFI_UNSUPPORTED
;
692 if ((BaseAddress
+ Length
) > BASE_4GB
) {
693 DEBUG ((DEBUG_ERROR
, "Beyond 4GB memory in 32-bit mode!\n"));
694 return EFI_UNSUPPORTED
;
697 case IMAGE_FILE_MACHINE_X64
:
698 ASSERT (CurrentPagingContext
.ContextData
.X64
.PageTableBase
!= 0);
702 return EFI_UNSUPPORTED
;
706 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
708 if (IsSplitted
!= NULL
) {
711 if (IsModified
!= NULL
) {
714 if (AllocatePagesFunc
== NULL
) {
715 AllocatePagesFunc
= AllocatePageTableMemory
;
719 // Make sure that the page table is changeable.
721 IsWpEnabled
= IsReadOnlyPageWriteProtected ();
723 DisableReadOnlyPageWriteProtect ();
727 // Below logic is to check 2M/4K page to make sure we donot waist memory.
729 Status
= EFI_SUCCESS
;
730 while (Length
!= 0) {
731 PageEntry
= GetPageTableEntry (&CurrentPagingContext
, BaseAddress
, &PageAttribute
);
732 if (PageEntry
== NULL
) {
733 Status
= RETURN_UNSUPPORTED
;
736 PageEntryLength
= PageAttributeToLength (PageAttribute
);
737 SplitAttribute
= NeedSplitPage (BaseAddress
, Length
, PageEntry
, PageAttribute
);
738 if (SplitAttribute
== PageNone
) {
739 ConvertPageEntryAttribute (&CurrentPagingContext
, PageEntry
, Attributes
, PageAction
, &IsEntryModified
);
740 if (IsEntryModified
) {
741 if (IsModified
!= NULL
) {
746 // Convert success, move to next
748 BaseAddress
+= PageEntryLength
;
749 Length
-= PageEntryLength
;
751 if (AllocatePagesFunc
== NULL
) {
752 Status
= RETURN_UNSUPPORTED
;
755 Status
= SplitPage (PageEntry
, PageAttribute
, SplitAttribute
, AllocatePagesFunc
);
756 if (RETURN_ERROR (Status
)) {
757 Status
= RETURN_UNSUPPORTED
;
760 if (IsSplitted
!= NULL
) {
763 if (IsModified
!= NULL
) {
767 // Just split current page
768 // Convert success in next around
775 // Restore page table write protection, if any.
778 EnableReadOnlyPageWriteProtect ();
784 This function assigns the page attributes for the memory region specified by BaseAddress and
785 Length from their current attributes to the attributes specified by Attributes.
787 Caller should make sure BaseAddress and Length is at page boundary.
789 Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
791 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.
792 @param[in] BaseAddress The physical address that is the start address of a memory region.
793 @param[in] Length The size in bytes of the memory region.
794 @param[in] Attributes The bit mask of attributes to set for the memory region.
795 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
796 NULL mean page split is unsupported.
798 @retval RETURN_SUCCESS The attributes were cleared for the memory region.
799 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
800 BaseAddress and Length cannot be modified.
801 @retval RETURN_INVALID_PARAMETER Length is zero.
802 Attributes specified an illegal combination of attributes that
803 cannot be set together.
804 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
805 the memory resource range.
806 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
807 resource range specified by BaseAddress and Length.
808 The bit mask of attributes is not support for the memory resource
809 range specified by BaseAddress and Length.
813 AssignMemoryPageAttributes (
814 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext OPTIONAL
,
815 IN PHYSICAL_ADDRESS BaseAddress
,
817 IN UINT64 Attributes
,
818 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL
821 RETURN_STATUS Status
;
825 // DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes));
826 Status
= ConvertMemoryPageAttributes (PagingContext
, BaseAddress
, Length
, Attributes
, PageActionAssign
, AllocatePagesFunc
, &IsSplitted
, &IsModified
);
827 if (!EFI_ERROR(Status
)) {
828 if ((PagingContext
== NULL
) && IsModified
) {
830 // Flush TLB as last step.
832 // Note: Since APs will always init CR3 register in HLT loop mode or do
833 // TLB flush in MWAIT loop mode, there's no need to flush TLB for them
844 Check if Execute Disable feature is enabled or not.
847 IsExecuteDisableEnabled (
851 MSR_CORE_IA32_EFER_REGISTER MsrEfer
;
853 MsrEfer
.Uint64
= AsmReadMsr64 (MSR_IA32_EFER
);
854 return (MsrEfer
.Bits
.NXE
== 1);
858 Update GCD memory space attributes according to current page table setup.
861 RefreshGcdMemoryAttributesFromPaging (
866 UINTN NumberOfDescriptors
;
867 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
868 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext
;
869 PAGE_ATTRIBUTE PageAttribute
;
872 UINT64 MemorySpaceLength
;
875 UINT64 PageStartAddress
;
878 UINT64 NewAttributes
;
882 // Assuming that memory space map returned is sorted already; otherwise sort
883 // them in the order of lowest address to highest address.
885 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
886 ASSERT_EFI_ERROR (Status
);
888 GetCurrentPagingContext (&PagingContext
);
895 if (IsExecuteDisableEnabled ()) {
896 Capabilities
= EFI_MEMORY_RO
| EFI_MEMORY_RP
| EFI_MEMORY_XP
;
898 Capabilities
= EFI_MEMORY_RO
| EFI_MEMORY_RP
;
901 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
902 if (MemorySpaceMap
[Index
].GcdMemoryType
== EfiGcdMemoryTypeNonExistent
) {
907 // Sync the actual paging related capabilities back to GCD service first.
908 // As a side effect (good one), this can also help to avoid unnecessary
909 // memory map entries due to the different capabilities of the same type
910 // memory, such as multiple RT_CODE and RT_DATA entries in memory map,
911 // which could cause boot failure of some old Linux distro (before v4.3).
913 Status
= gDS
->SetMemorySpaceCapabilities (
914 MemorySpaceMap
[Index
].BaseAddress
,
915 MemorySpaceMap
[Index
].Length
,
916 MemorySpaceMap
[Index
].Capabilities
| Capabilities
918 if (EFI_ERROR (Status
)) {
920 // If we cannot udpate the capabilities, we cannot update its
921 // attributes either. So just simply skip current block of memory.
925 "Failed to update capability: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",
926 (UINT64
)Index
, MemorySpaceMap
[Index
].BaseAddress
,
927 MemorySpaceMap
[Index
].BaseAddress
+ MemorySpaceMap
[Index
].Length
- 1,
928 MemorySpaceMap
[Index
].Capabilities
,
929 MemorySpaceMap
[Index
].Capabilities
| Capabilities
934 if (MemorySpaceMap
[Index
].BaseAddress
>= (BaseAddress
+ PageLength
)) {
936 // Current memory space starts at a new page. Resetting PageLength will
937 // trigger a retrieval of page attributes at new address.
942 // In case current memory space is not adjacent to last one
944 PageLength
-= (MemorySpaceMap
[Index
].BaseAddress
- BaseAddress
);
948 // Sync actual page attributes to GCD
950 BaseAddress
= MemorySpaceMap
[Index
].BaseAddress
;
951 MemorySpaceLength
= MemorySpaceMap
[Index
].Length
;
952 while (MemorySpaceLength
> 0) {
953 if (PageLength
== 0) {
954 PageEntry
= GetPageTableEntry (&PagingContext
, BaseAddress
, &PageAttribute
);
955 if (PageEntry
== NULL
) {
960 // Note current memory space might start in the middle of a page
962 PageStartAddress
= (*PageEntry
) & (UINT64
)PageAttributeToMask(PageAttribute
);
963 PageLength
= PageAttributeToLength (PageAttribute
) - (BaseAddress
- PageStartAddress
);
964 Attributes
= GetAttributesFromPageEntry (PageEntry
);
967 Length
= MIN (PageLength
, MemorySpaceLength
);
968 if (Attributes
!= (MemorySpaceMap
[Index
].Attributes
&
969 EFI_MEMORY_PAGETYPE_MASK
)) {
970 NewAttributes
= (MemorySpaceMap
[Index
].Attributes
&
971 ~EFI_MEMORY_PAGETYPE_MASK
) | Attributes
;
972 Status
= gDS
->SetMemorySpaceAttributes (
977 ASSERT_EFI_ERROR (Status
);
980 "Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",
981 (UINT64
)Index
, BaseAddress
, BaseAddress
+ Length
- 1,
982 MemorySpaceMap
[Index
].Attributes
,
987 PageLength
-= Length
;
988 MemorySpaceLength
-= Length
;
989 BaseAddress
+= Length
;
993 FreePool (MemorySpaceMap
);
997 Initialize a buffer pool for page table use only.
999 To reduce the potential split operation on page table, the pages reserved for
1000 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
1001 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
1002 initialized with number of pages greater than or equal to the given PoolPages.
1004 Once the pages in the pool are used up, this method should be called again to
1005 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. Usually this won't happen
1008 @param[in] PoolPages The least page number of the pool to be created.
1010 @retval TRUE The pool is initialized successfully.
1011 @retval FALSE The memory is out of resource.
1014 InitializePageTablePool (
1022 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
1025 PoolPages
+= 1; // Add one page for header.
1026 PoolPages
= ((PoolPages
- 1) / PAGE_TABLE_POOL_UNIT_PAGES
+ 1) *
1027 PAGE_TABLE_POOL_UNIT_PAGES
;
1028 Buffer
= AllocateAlignedPages (PoolPages
, PAGE_TABLE_POOL_ALIGNMENT
);
1029 if (Buffer
== NULL
) {
1030 DEBUG ((DEBUG_ERROR
, "ERROR: Out of aligned pages\r\n"));
1035 // Link all pools into a list for easier track later.
1037 if (mPageTablePool
== NULL
) {
1038 mPageTablePool
= Buffer
;
1039 mPageTablePool
->NextPool
= mPageTablePool
;
1041 ((PAGE_TABLE_POOL
*)Buffer
)->NextPool
= mPageTablePool
->NextPool
;
1042 mPageTablePool
->NextPool
= Buffer
;
1043 mPageTablePool
= Buffer
;
1047 // Reserve one page for pool header.
1049 mPageTablePool
->FreePages
= PoolPages
- 1;
1050 mPageTablePool
->Offset
= EFI_PAGES_TO_SIZE (1);
1053 // Mark the whole pool pages as read-only.
1055 ConvertMemoryPageAttributes (
1057 (PHYSICAL_ADDRESS
)(UINTN
)Buffer
,
1058 EFI_PAGES_TO_SIZE (PoolPages
),
1061 AllocatePageTableMemory
,
1065 ASSERT (IsModified
== TRUE
);
1071 This API provides a way to allocate memory for page table.
1073 This API can be called more than once to allocate memory for page tables.
1075 Allocates the number of 4KB pages and returns a pointer to the allocated
1076 buffer. The buffer returned is aligned on a 4KB boundary.
1078 If Pages is 0, then NULL is returned.
1079 If there is not enough memory remaining to satisfy the request, then NULL is
1082 @param Pages The number of 4 KB pages to allocate.
1084 @return A pointer to the allocated buffer or NULL if allocation fails.
1089 AllocatePageTableMemory (
1100 // Renew the pool if necessary.
1102 if (mPageTablePool
== NULL
||
1103 Pages
> mPageTablePool
->FreePages
) {
1104 if (!InitializePageTablePool (Pages
)) {
1109 Buffer
= (UINT8
*)mPageTablePool
+ mPageTablePool
->Offset
;
1111 mPageTablePool
->Offset
+= EFI_PAGES_TO_SIZE (Pages
);
1112 mPageTablePool
->FreePages
-= Pages
;
1118 Initialize the Page Table lib.
1121 InitializePageTableLib (
1125 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext
;
1127 GetCurrentPagingContext (&CurrentPagingContext
);
1130 // Reserve memory of page tables for future uses, if paging is enabled.
1132 if (CurrentPagingContext
.ContextData
.X64
.PageTableBase
!= 0 &&
1133 (CurrentPagingContext
.ContextData
.Ia32
.Attributes
&
1134 PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
) != 0) {
1135 DisableReadOnlyPageWriteProtect ();
1136 InitializePageTablePool (1);
1137 EnableReadOnlyPageWriteProtect ();
1140 DEBUG ((DEBUG_INFO
, "CurrentPagingContext:\n", CurrentPagingContext
.MachineType
));
1141 DEBUG ((DEBUG_INFO
, " MachineType - 0x%x\n", CurrentPagingContext
.MachineType
));
1142 DEBUG ((DEBUG_INFO
, " PageTableBase - 0x%x\n", CurrentPagingContext
.ContextData
.X64
.PageTableBase
));
1143 DEBUG ((DEBUG_INFO
, " Attributes - 0x%x\n", CurrentPagingContext
.ContextData
.X64
.Attributes
));