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>
28 #include "CpuPageTable.h"
33 #define IA32_PG_P BIT0
34 #define IA32_PG_RW BIT1
35 #define IA32_PG_U BIT2
36 #define IA32_PG_WT BIT3
37 #define IA32_PG_CD BIT4
38 #define IA32_PG_A BIT5
39 #define IA32_PG_D BIT6
40 #define IA32_PG_PS BIT7
41 #define IA32_PG_PAT_2M BIT12
42 #define IA32_PG_PAT_4K IA32_PG_PS
43 #define IA32_PG_PMNT BIT62
44 #define IA32_PG_NX BIT63
46 #define PAGE_ATTRIBUTE_BITS (IA32_PG_D | IA32_PG_A | IA32_PG_U | IA32_PG_RW | IA32_PG_P)
48 // Bits 1, 2, 5, 6 are reserved in the IA32 PAE PDPTE
49 // X64 PAE PDPTE does not have such restriction
51 #define IA32_PAE_PDPTE_ATTRIBUTE_BITS (IA32_PG_P)
53 #define PAGE_PROGATE_BITS (IA32_PG_NX | PAGE_ATTRIBUTE_BITS)
55 #define PAGING_4K_MASK 0xFFF
56 #define PAGING_2M_MASK 0x1FFFFF
57 #define PAGING_1G_MASK 0x3FFFFFFF
59 #define PAGING_PAE_INDEX_MASK 0x1FF
61 #define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull
62 #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull
63 #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull
73 PAGE_ATTRIBUTE Attribute
;
76 } PAGE_ATTRIBUTE_TABLE
;
84 PAGE_ATTRIBUTE_TABLE mPageAttributeTable
[] = {
85 {Page4K
, SIZE_4KB
, PAGING_4K_ADDRESS_MASK_64
},
86 {Page2M
, SIZE_2MB
, PAGING_2M_ADDRESS_MASK_64
},
87 {Page1G
, SIZE_1GB
, PAGING_1G_ADDRESS_MASK_64
},
91 Enable write protection function for AP.
93 @param[in,out] Buffer The pointer to private data buffer.
97 SyncCpuEnableWriteProtection (
101 AsmWriteCr0 (AsmReadCr0 () | BIT16
);
105 CpuFlushTlb function for AP.
107 @param[in,out] Buffer The pointer to private data buffer.
119 Sync memory page attributes for AP.
121 @param[in] Procedure A pointer to the function to be run on enabled APs of
125 SyncMemoryPageAttributesAp (
126 IN EFI_AP_PROCEDURE Procedure
130 EFI_MP_SERVICES_PROTOCOL
*MpService
;
132 Status
= gBS
->LocateProtocol (
133 &gEfiMpServiceProtocolGuid
,
138 // Synchronize the update with all APs
140 if (!EFI_ERROR (Status
)) {
141 Status
= MpService
->StartupAllAPs (
143 Procedure
, // Procedure
144 FALSE
, // SingleThread
146 0, // TimeoutInMicrosecsond
147 NULL
, // ProcedureArgument
148 NULL
// FailedCpuList
150 ASSERT (Status
== EFI_SUCCESS
|| Status
== EFI_NOT_STARTED
|| Status
== EFI_NOT_READY
);
155 Return current paging context.
157 @param[in,out] PagingContext The paging context.
160 GetCurrentPagingContext (
161 IN OUT PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
167 ZeroMem(PagingContext
, sizeof(*PagingContext
));
168 if (sizeof(UINTN
) == sizeof(UINT64
)) {
169 PagingContext
->MachineType
= IMAGE_FILE_MACHINE_X64
;
171 PagingContext
->MachineType
= IMAGE_FILE_MACHINE_I386
;
173 if ((AsmReadCr0 () & BIT31
) != 0) {
174 PagingContext
->ContextData
.X64
.PageTableBase
= (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
);
175 if ((AsmReadCr0 () & BIT16
) == 0) {
176 AsmWriteCr0 (AsmReadCr0 () | BIT16
);
177 SyncMemoryPageAttributesAp (SyncCpuEnableWriteProtection
);
180 PagingContext
->ContextData
.X64
.PageTableBase
= 0;
183 if ((AsmReadCr4 () & BIT4
) != 0) {
184 PagingContext
->ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PSE
;
186 if ((AsmReadCr4 () & BIT5
) != 0) {
187 PagingContext
->ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
;
189 if ((AsmReadCr0 () & BIT16
) != 0) {
190 PagingContext
->ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_WP_ENABLE
;
193 AsmCpuid (0x80000000, &RegEax
, NULL
, NULL
, NULL
);
194 if (RegEax
> 0x80000000) {
195 AsmCpuid (0x80000001, NULL
, NULL
, NULL
, &RegEdx
);
196 if ((RegEdx
& BIT20
) != 0) {
198 if ((AsmReadMsr64 (0xC0000080) & BIT11
) != 0) {
200 PagingContext
->ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED
;
203 if ((RegEdx
& BIT26
) != 0) {
204 PagingContext
->ContextData
.Ia32
.Attributes
|= PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAGE_1G_SUPPORT
;
210 Return length according to page attributes.
212 @param[in] PageAttributes The page attribute of the page entry.
214 @return The length of page entry.
217 PageAttributeToLength (
218 IN PAGE_ATTRIBUTE PageAttribute
222 for (Index
= 0; Index
< sizeof(mPageAttributeTable
)/sizeof(mPageAttributeTable
[0]); Index
++) {
223 if (PageAttribute
== mPageAttributeTable
[Index
].Attribute
) {
224 return (UINTN
)mPageAttributeTable
[Index
].Length
;
231 Return address mask according to page attributes.
233 @param[in] PageAttributes The page attribute of the page entry.
235 @return The address mask of page entry.
238 PageAttributeToMask (
239 IN PAGE_ATTRIBUTE PageAttribute
243 for (Index
= 0; Index
< sizeof(mPageAttributeTable
)/sizeof(mPageAttributeTable
[0]); Index
++) {
244 if (PageAttribute
== mPageAttributeTable
[Index
].Attribute
) {
245 return (UINTN
)mPageAttributeTable
[Index
].AddressMask
;
252 Return page table entry to match the address.
254 @param[in] PagingContext The paging context.
255 @param[in] Address The address to be checked.
256 @param[out] PageAttributes The page attribute of the page entry.
258 @return The page entry.
262 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
,
263 IN PHYSICAL_ADDRESS Address
,
264 OUT PAGE_ATTRIBUTE
*PageAttribute
275 UINT64 AddressEncMask
;
277 ASSERT (PagingContext
!= NULL
);
279 Index4
= ((UINTN
)RShiftU64 (Address
, 39)) & PAGING_PAE_INDEX_MASK
;
280 Index3
= ((UINTN
)Address
>> 30) & PAGING_PAE_INDEX_MASK
;
281 Index2
= ((UINTN
)Address
>> 21) & PAGING_PAE_INDEX_MASK
;
282 Index1
= ((UINTN
)Address
>> 12) & PAGING_PAE_INDEX_MASK
;
284 // Make sure AddressEncMask is contained to smallest supported address field.
286 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
288 if (PagingContext
->MachineType
== IMAGE_FILE_MACHINE_X64
) {
289 L4PageTable
= (UINT64
*)(UINTN
)PagingContext
->ContextData
.X64
.PageTableBase
;
290 if (L4PageTable
[Index4
] == 0) {
291 *PageAttribute
= PageNone
;
295 L3PageTable
= (UINT64
*)(UINTN
)(L4PageTable
[Index4
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
297 ASSERT((PagingContext
->ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
) != 0);
298 L3PageTable
= (UINT64
*)(UINTN
)PagingContext
->ContextData
.Ia32
.PageTableBase
;
300 if (L3PageTable
[Index3
] == 0) {
301 *PageAttribute
= PageNone
;
304 if ((L3PageTable
[Index3
] & IA32_PG_PS
) != 0) {
306 *PageAttribute
= Page1G
;
307 return &L3PageTable
[Index3
];
310 L2PageTable
= (UINT64
*)(UINTN
)(L3PageTable
[Index3
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
311 if (L2PageTable
[Index2
] == 0) {
312 *PageAttribute
= PageNone
;
315 if ((L2PageTable
[Index2
] & IA32_PG_PS
) != 0) {
317 *PageAttribute
= Page2M
;
318 return &L2PageTable
[Index2
];
322 L1PageTable
= (UINT64
*)(UINTN
)(L2PageTable
[Index2
] & ~AddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
323 if ((L1PageTable
[Index1
] == 0) && (Address
!= 0)) {
324 *PageAttribute
= PageNone
;
327 *PageAttribute
= Page4K
;
328 return &L1PageTable
[Index1
];
332 Return memory attributes of page entry.
334 @param[in] PageEntry The page entry.
336 @return Memory attributes of page entry.
339 GetAttributesFromPageEntry (
345 if ((*PageEntry
& IA32_PG_P
) == 0) {
346 Attributes
|= EFI_MEMORY_RP
;
348 if ((*PageEntry
& IA32_PG_RW
) == 0) {
349 Attributes
|= EFI_MEMORY_RO
;
351 if ((*PageEntry
& IA32_PG_NX
) != 0) {
352 Attributes
|= EFI_MEMORY_XP
;
358 Modify memory attributes of page entry.
360 @param[in] PagingContext The paging context.
361 @param[in] PageEntry The page entry.
362 @param[in] Attributes The bit mask of attributes to modify for the memory region.
363 @param[in] PageAction The page action.
364 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
367 ConvertPageEntryAttribute (
368 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext
,
369 IN UINT64
*PageEntry
,
370 IN UINT64 Attributes
,
371 IN PAGE_ACTION PageAction
,
372 OUT BOOLEAN
*IsModified
375 UINT64 CurrentPageEntry
;
378 CurrentPageEntry
= *PageEntry
;
379 NewPageEntry
= CurrentPageEntry
;
380 if ((Attributes
& EFI_MEMORY_RP
) != 0) {
381 switch (PageAction
) {
382 case PageActionAssign
:
384 NewPageEntry
&= ~(UINT64
)IA32_PG_P
;
386 case PageActionClear
:
387 NewPageEntry
|= IA32_PG_P
;
391 switch (PageAction
) {
392 case PageActionAssign
:
393 NewPageEntry
|= IA32_PG_P
;
396 case PageActionClear
:
400 if ((Attributes
& EFI_MEMORY_RO
) != 0) {
401 switch (PageAction
) {
402 case PageActionAssign
:
404 NewPageEntry
&= ~(UINT64
)IA32_PG_RW
;
406 case PageActionClear
:
407 NewPageEntry
|= IA32_PG_RW
;
411 switch (PageAction
) {
412 case PageActionAssign
:
413 NewPageEntry
|= IA32_PG_RW
;
416 case PageActionClear
:
420 if ((PagingContext
->ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_XD_ACTIVATED
) != 0) {
421 if ((Attributes
& EFI_MEMORY_XP
) != 0) {
422 switch (PageAction
) {
423 case PageActionAssign
:
425 NewPageEntry
|= IA32_PG_NX
;
427 case PageActionClear
:
428 NewPageEntry
&= ~IA32_PG_NX
;
432 switch (PageAction
) {
433 case PageActionAssign
:
434 NewPageEntry
&= ~IA32_PG_NX
;
437 case PageActionClear
:
442 *PageEntry
= NewPageEntry
;
443 if (CurrentPageEntry
!= NewPageEntry
) {
445 DEBUG ((DEBUG_VERBOSE
, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry
));
446 DEBUG ((DEBUG_VERBOSE
, "->0x%lx\n", NewPageEntry
));
453 This function returns if there is need to split page entry.
455 @param[in] BaseAddress The base address to be checked.
456 @param[in] Length The length to be checked.
457 @param[in] PageEntry The page entry to be checked.
458 @param[in] PageAttribute The page attribute of the page entry.
460 @retval SplitAttributes on if there is need to split page entry.
464 IN PHYSICAL_ADDRESS BaseAddress
,
466 IN UINT64
*PageEntry
,
467 IN PAGE_ATTRIBUTE PageAttribute
470 UINT64 PageEntryLength
;
472 PageEntryLength
= PageAttributeToLength (PageAttribute
);
474 if (((BaseAddress
& (PageEntryLength
- 1)) == 0) && (Length
>= PageEntryLength
)) {
478 if (((BaseAddress
& PAGING_2M_MASK
) != 0) || (Length
< SIZE_2MB
)) {
486 This function splits one page entry to small page entries.
488 @param[in] PageEntry The page entry to be splitted.
489 @param[in] PageAttribute The page attribute of the page entry.
490 @param[in] SplitAttribute How to split the page entry.
491 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
493 @retval RETURN_SUCCESS The page entry is splitted.
494 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
495 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
499 IN UINT64
*PageEntry
,
500 IN PAGE_ATTRIBUTE PageAttribute
,
501 IN PAGE_ATTRIBUTE SplitAttribute
,
502 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc
506 UINT64
*NewPageEntry
;
508 UINT64 AddressEncMask
;
510 ASSERT (PageAttribute
== Page2M
|| PageAttribute
== Page1G
);
512 ASSERT (AllocatePagesFunc
!= NULL
);
514 // Make sure AddressEncMask is contained to smallest supported address field.
516 AddressEncMask
= PcdGet64 (PcdPteMemoryEncryptionAddressOrMask
) & PAGING_1G_ADDRESS_MASK_64
;
518 if (PageAttribute
== Page2M
) {
522 ASSERT (SplitAttribute
== Page4K
);
523 if (SplitAttribute
== Page4K
) {
524 NewPageEntry
= AllocatePagesFunc (1);
525 DEBUG ((DEBUG_INFO
, "Split - 0x%x\n", NewPageEntry
));
526 if (NewPageEntry
== NULL
) {
527 return RETURN_OUT_OF_RESOURCES
;
529 BaseAddress
= *PageEntry
& ~AddressEncMask
& PAGING_2M_ADDRESS_MASK_64
;
530 for (Index
= 0; Index
< SIZE_4KB
/ sizeof(UINT64
); Index
++) {
531 NewPageEntry
[Index
] = (BaseAddress
+ SIZE_4KB
* Index
) | AddressEncMask
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
533 (*PageEntry
) = (UINT64
)(UINTN
)NewPageEntry
| AddressEncMask
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
534 return RETURN_SUCCESS
;
536 return RETURN_UNSUPPORTED
;
538 } else if (PageAttribute
== Page1G
) {
541 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
543 ASSERT (SplitAttribute
== Page2M
|| SplitAttribute
== Page4K
);
544 if ((SplitAttribute
== Page2M
|| SplitAttribute
== Page4K
)) {
545 NewPageEntry
= AllocatePagesFunc (1);
546 DEBUG ((DEBUG_INFO
, "Split - 0x%x\n", NewPageEntry
));
547 if (NewPageEntry
== NULL
) {
548 return RETURN_OUT_OF_RESOURCES
;
550 BaseAddress
= *PageEntry
& ~AddressEncMask
& PAGING_1G_ADDRESS_MASK_64
;
551 for (Index
= 0; Index
< SIZE_4KB
/ sizeof(UINT64
); Index
++) {
552 NewPageEntry
[Index
] = (BaseAddress
+ SIZE_2MB
* Index
) | AddressEncMask
| IA32_PG_PS
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
554 (*PageEntry
) = (UINT64
)(UINTN
)NewPageEntry
| AddressEncMask
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
555 return RETURN_SUCCESS
;
557 return RETURN_UNSUPPORTED
;
560 return RETURN_UNSUPPORTED
;
565 This function modifies the page attributes for the memory region specified by BaseAddress and
566 Length from their current attributes to the attributes specified by Attributes.
568 Caller should make sure BaseAddress and Length is at page boundary.
570 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.
571 @param[in] BaseAddress The physical address that is the start address of a memory region.
572 @param[in] Length The size in bytes of the memory region.
573 @param[in] Attributes The bit mask of attributes to modify for the memory region.
574 @param[in] PageAction The page action.
575 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
576 NULL mean page split is unsupported.
577 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
578 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
580 @retval RETURN_SUCCESS The attributes were modified for the memory region.
581 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
582 BaseAddress and Length cannot be modified.
583 @retval RETURN_INVALID_PARAMETER Length is zero.
584 Attributes specified an illegal combination of attributes that
585 cannot be set together.
586 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
587 the memory resource range.
588 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
589 resource range specified by BaseAddress and Length.
590 The bit mask of attributes is not support for the memory resource
591 range specified by BaseAddress and Length.
594 ConvertMemoryPageAttributes (
595 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext OPTIONAL
,
596 IN PHYSICAL_ADDRESS BaseAddress
,
598 IN UINT64 Attributes
,
599 IN PAGE_ACTION PageAction
,
600 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL
,
601 OUT BOOLEAN
*IsSplitted
, OPTIONAL
602 OUT BOOLEAN
*IsModified OPTIONAL
605 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext
;
607 PAGE_ATTRIBUTE PageAttribute
;
608 UINTN PageEntryLength
;
609 PAGE_ATTRIBUTE SplitAttribute
;
610 RETURN_STATUS Status
;
611 BOOLEAN IsEntryModified
;
613 if ((BaseAddress
& (SIZE_4KB
- 1)) != 0) {
614 DEBUG ((DEBUG_ERROR
, "BaseAddress(0x%lx) is not aligned!\n", BaseAddress
));
615 return EFI_UNSUPPORTED
;
617 if ((Length
& (SIZE_4KB
- 1)) != 0) {
618 DEBUG ((DEBUG_ERROR
, "Length(0x%lx) is not aligned!\n", Length
));
619 return EFI_UNSUPPORTED
;
622 DEBUG ((DEBUG_ERROR
, "Length is 0!\n"));
623 return RETURN_INVALID_PARAMETER
;
626 if ((Attributes
& ~(EFI_MEMORY_RP
| EFI_MEMORY_RO
| EFI_MEMORY_XP
)) != 0) {
627 DEBUG ((DEBUG_ERROR
, "Attributes(0x%lx) has unsupported bit\n", Attributes
));
628 return EFI_UNSUPPORTED
;
631 if (PagingContext
== NULL
) {
632 GetCurrentPagingContext (&CurrentPagingContext
);
634 CopyMem (&CurrentPagingContext
, PagingContext
, sizeof(CurrentPagingContext
));
636 switch(CurrentPagingContext
.MachineType
) {
637 case IMAGE_FILE_MACHINE_I386
:
638 if (CurrentPagingContext
.ContextData
.Ia32
.PageTableBase
== 0) {
639 if (Attributes
== 0) {
642 DEBUG ((DEBUG_ERROR
, "PageTable is 0!\n"));
643 return EFI_UNSUPPORTED
;
646 if ((CurrentPagingContext
.ContextData
.Ia32
.Attributes
& PAGE_TABLE_LIB_PAGING_CONTEXT_IA32_X64_ATTRIBUTES_PAE
) == 0) {
647 DEBUG ((DEBUG_ERROR
, "Non-PAE Paging!\n"));
648 return EFI_UNSUPPORTED
;
651 case IMAGE_FILE_MACHINE_X64
:
652 ASSERT (CurrentPagingContext
.ContextData
.X64
.PageTableBase
!= 0);
656 return EFI_UNSUPPORTED
;
660 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
662 if (IsSplitted
!= NULL
) {
665 if (IsModified
!= NULL
) {
670 // Below logic is to check 2M/4K page to make sure we donot waist memory.
672 while (Length
!= 0) {
673 PageEntry
= GetPageTableEntry (&CurrentPagingContext
, BaseAddress
, &PageAttribute
);
674 if (PageEntry
== NULL
) {
675 return RETURN_UNSUPPORTED
;
677 PageEntryLength
= PageAttributeToLength (PageAttribute
);
678 SplitAttribute
= NeedSplitPage (BaseAddress
, Length
, PageEntry
, PageAttribute
);
679 if (SplitAttribute
== PageNone
) {
680 ConvertPageEntryAttribute (&CurrentPagingContext
, PageEntry
, Attributes
, PageAction
, &IsEntryModified
);
681 if (IsEntryModified
) {
682 if (IsModified
!= NULL
) {
687 // Convert success, move to next
689 BaseAddress
+= PageEntryLength
;
690 Length
-= PageEntryLength
;
692 if (AllocatePagesFunc
== NULL
) {
693 return RETURN_UNSUPPORTED
;
695 Status
= SplitPage (PageEntry
, PageAttribute
, SplitAttribute
, AllocatePagesFunc
);
696 if (RETURN_ERROR (Status
)) {
697 return RETURN_UNSUPPORTED
;
699 if (IsSplitted
!= NULL
) {
702 if (IsModified
!= NULL
) {
706 // Just split current page
707 // Convert success in next around
712 return RETURN_SUCCESS
;
716 This function assigns the page attributes for the memory region specified by BaseAddress and
717 Length from their current attributes to the attributes specified by Attributes.
719 Caller should make sure BaseAddress and Length is at page boundary.
721 Caller need guarentee the TPL <= TPL_NOTIFY, if there is split page request.
723 @param[in] PagingContext The paging context. NULL means get page table from current CPU context.
724 @param[in] BaseAddress The physical address that is the start address of a memory region.
725 @param[in] Length The size in bytes of the memory region.
726 @param[in] Attributes The bit mask of attributes to set for the memory region.
727 @param[in] AllocatePagesFunc If page split is needed, this function is used to allocate more pages.
728 NULL mean page split is unsupported.
730 @retval RETURN_SUCCESS The attributes were cleared for the memory region.
731 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
732 BaseAddress and Length cannot be modified.
733 @retval RETURN_INVALID_PARAMETER Length is zero.
734 Attributes specified an illegal combination of attributes that
735 cannot be set together.
736 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
737 the memory resource range.
738 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
739 resource range specified by BaseAddress and Length.
740 The bit mask of attributes is not support for the memory resource
741 range specified by BaseAddress and Length.
745 AssignMemoryPageAttributes (
746 IN PAGE_TABLE_LIB_PAGING_CONTEXT
*PagingContext OPTIONAL
,
747 IN PHYSICAL_ADDRESS BaseAddress
,
749 IN UINT64 Attributes
,
750 IN PAGE_TABLE_LIB_ALLOCATE_PAGES AllocatePagesFunc OPTIONAL
753 RETURN_STATUS Status
;
757 // DEBUG((DEBUG_INFO, "AssignMemoryPageAttributes: 0x%lx - 0x%lx (0x%lx)\n", BaseAddress, Length, Attributes));
758 Status
= ConvertMemoryPageAttributes (PagingContext
, BaseAddress
, Length
, Attributes
, PageActionAssign
, AllocatePagesFunc
, &IsSplitted
, &IsModified
);
759 if (!EFI_ERROR(Status
)) {
760 if ((PagingContext
== NULL
) && IsModified
) {
762 // Flush TLB as last step
765 SyncMemoryPageAttributesAp (SyncCpuFlushTlb
);
773 Check if Execute Disable feature is enabled or not.
776 IsExecuteDisableEnabled (
780 MSR_CORE_IA32_EFER_REGISTER MsrEfer
;
782 MsrEfer
.Uint64
= AsmReadMsr64 (MSR_IA32_EFER
);
783 return (MsrEfer
.Bits
.NXE
== 1);
787 Update GCD memory space attributes according to current page table setup.
790 RefreshGcdMemoryAttributesFromPaging (
795 UINTN NumberOfDescriptors
;
796 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemorySpaceMap
;
797 PAGE_TABLE_LIB_PAGING_CONTEXT PagingContext
;
798 PAGE_ATTRIBUTE PageAttribute
;
801 UINT64 MemorySpaceLength
;
804 UINT64 PageStartAddress
;
807 UINT64 NewAttributes
;
811 // Assuming that memory space map returned is sorted already; otherwise sort
812 // them in the order of lowest address to highest address.
814 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemorySpaceMap
);
815 ASSERT_EFI_ERROR (Status
);
817 GetCurrentPagingContext (&PagingContext
);
824 if (IsExecuteDisableEnabled ()) {
825 Capabilities
= EFI_MEMORY_RO
| EFI_MEMORY_RP
| EFI_MEMORY_XP
;
827 Capabilities
= EFI_MEMORY_RO
| EFI_MEMORY_RP
;
830 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
831 if (MemorySpaceMap
[Index
].GcdMemoryType
== EfiGcdMemoryTypeNonExistent
) {
836 // Sync the actual paging related capabilities back to GCD service first.
837 // As a side effect (good one), this can also help to avoid unnecessary
838 // memory map entries due to the different capabilities of the same type
839 // memory, such as multiple RT_CODE and RT_DATA entries in memory map,
840 // which could cause boot failure of some old Linux distro (before v4.3).
842 Status
= gDS
->SetMemorySpaceCapabilities (
843 MemorySpaceMap
[Index
].BaseAddress
,
844 MemorySpaceMap
[Index
].Length
,
845 MemorySpaceMap
[Index
].Capabilities
| Capabilities
847 if (EFI_ERROR (Status
)) {
849 // If we cannot udpate the capabilities, we cannot update its
850 // attributes either. So just simply skip current block of memory.
854 "Failed to update capability: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",
855 (UINT64
)Index
, MemorySpaceMap
[Index
].BaseAddress
,
856 MemorySpaceMap
[Index
].BaseAddress
+ MemorySpaceMap
[Index
].Length
- 1,
857 MemorySpaceMap
[Index
].Capabilities
,
858 MemorySpaceMap
[Index
].Capabilities
| Capabilities
863 if (MemorySpaceMap
[Index
].BaseAddress
>= (BaseAddress
+ PageLength
)) {
865 // Current memory space starts at a new page. Resetting PageLength will
866 // trigger a retrieval of page attributes at new address.
871 // In case current memory space is not adjacent to last one
873 PageLength
-= (MemorySpaceMap
[Index
].BaseAddress
- BaseAddress
);
877 // Sync actual page attributes to GCD
879 BaseAddress
= MemorySpaceMap
[Index
].BaseAddress
;
880 MemorySpaceLength
= MemorySpaceMap
[Index
].Length
;
881 while (MemorySpaceLength
> 0) {
882 if (PageLength
== 0) {
883 PageEntry
= GetPageTableEntry (&PagingContext
, BaseAddress
, &PageAttribute
);
884 if (PageEntry
== NULL
) {
889 // Note current memory space might start in the middle of a page
891 PageStartAddress
= (*PageEntry
) & (UINT64
)PageAttributeToMask(PageAttribute
);
892 PageLength
= PageAttributeToLength (PageAttribute
) - (BaseAddress
- PageStartAddress
);
893 Attributes
= GetAttributesFromPageEntry (PageEntry
);
896 Length
= MIN (PageLength
, MemorySpaceLength
);
897 if (Attributes
!= (MemorySpaceMap
[Index
].Attributes
&
898 EFI_MEMORY_PAGETYPE_MASK
)) {
899 NewAttributes
= (MemorySpaceMap
[Index
].Attributes
&
900 ~EFI_MEMORY_PAGETYPE_MASK
) | Attributes
;
901 Status
= gDS
->SetMemorySpaceAttributes (
906 ASSERT_EFI_ERROR (Status
);
909 "Updated memory space attribute: [%lu] %016lx - %016lx (%016lx -> %016lx)\r\n",
910 (UINT64
)Index
, BaseAddress
, BaseAddress
+ Length
- 1,
911 MemorySpaceMap
[Index
].Attributes
,
916 PageLength
-= Length
;
917 MemorySpaceLength
-= Length
;
918 BaseAddress
+= Length
;
922 FreePool (MemorySpaceMap
);
926 Initialize the Page Table lib.
929 InitializePageTableLib (
933 PAGE_TABLE_LIB_PAGING_CONTEXT CurrentPagingContext
;
935 GetCurrentPagingContext (&CurrentPagingContext
);
936 DEBUG ((DEBUG_INFO
, "CurrentPagingContext:\n", CurrentPagingContext
.MachineType
));
937 DEBUG ((DEBUG_INFO
, " MachineType - 0x%x\n", CurrentPagingContext
.MachineType
));
938 DEBUG ((DEBUG_INFO
, " PageTableBase - 0x%x\n", CurrentPagingContext
.ContextData
.X64
.PageTableBase
));
939 DEBUG ((DEBUG_INFO
, " Attributes - 0x%x\n", CurrentPagingContext
.ContextData
.X64
.Attributes
));