3 Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
8 #include "PiSmmCpuDxeSmm.h"
11 // attributes for reserved memory before it is promoted to system memory
13 #define EFI_MEMORY_PRESENT 0x0100000000000000ULL
14 #define EFI_MEMORY_INITIALIZED 0x0200000000000000ULL
15 #define EFI_MEMORY_TESTED 0x0400000000000000ULL
17 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
18 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
20 EFI_MEMORY_DESCRIPTOR
*mUefiMemoryMap
;
21 UINTN mUefiMemoryMapSize
;
22 UINTN mUefiDescriptorSize
;
24 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*mGcdMemSpace
= NULL
;
25 UINTN mGcdMemNumberOfDesc
= 0;
27 EFI_MEMORY_ATTRIBUTES_TABLE
*mUefiMemoryAttributesTable
= NULL
;
29 PAGE_ATTRIBUTE_TABLE mPageAttributeTable
[] = {
30 { Page4K
, SIZE_4KB
, PAGING_4K_ADDRESS_MASK_64
},
31 { Page2M
, SIZE_2MB
, PAGING_2M_ADDRESS_MASK_64
},
32 { Page1G
, SIZE_1GB
, PAGING_1G_ADDRESS_MASK_64
},
35 BOOLEAN mIsShadowStack
= FALSE
;
36 BOOLEAN m5LevelPagingNeeded
= FALSE
;
39 // Global variable to keep track current available memory used as page table.
41 PAGE_TABLE_POOL
*mPageTablePool
= NULL
;
44 // If memory used by SMM page table has been mareked as ReadOnly.
46 BOOLEAN mIsReadOnlyPageTable
= FALSE
;
49 Initialize a buffer pool for page table use only.
51 To reduce the potential split operation on page table, the pages reserved for
52 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
53 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
54 initialized with number of pages greater than or equal to the given PoolPages.
56 Once the pages in the pool are used up, this method should be called again to
57 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. But usually this won't
60 @param PoolPages The least page number of the pool to be created.
62 @retval TRUE The pool is initialized successfully.
63 @retval FALSE The memory is out of resource.
66 InitializePageTablePool (
76 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
79 PoolPages
+= 1; // Add one page for header.
80 PoolPages
= ((PoolPages
- 1) / PAGE_TABLE_POOL_UNIT_PAGES
+ 1) *
81 PAGE_TABLE_POOL_UNIT_PAGES
;
82 Buffer
= AllocateAlignedPages (PoolPages
, PAGE_TABLE_POOL_ALIGNMENT
);
84 DEBUG ((DEBUG_ERROR
, "ERROR: Out of aligned pages\r\n"));
89 // Link all pools into a list for easier track later.
91 if (mPageTablePool
== NULL
) {
92 mPageTablePool
= Buffer
;
93 mPageTablePool
->NextPool
= mPageTablePool
;
95 ((PAGE_TABLE_POOL
*)Buffer
)->NextPool
= mPageTablePool
->NextPool
;
96 mPageTablePool
->NextPool
= Buffer
;
97 mPageTablePool
= Buffer
;
101 // Reserve one page for pool header.
103 mPageTablePool
->FreePages
= PoolPages
- 1;
104 mPageTablePool
->Offset
= EFI_PAGES_TO_SIZE (1);
107 // If page table memory has been marked as RO, mark the new pool pages as read-only.
109 if (mIsReadOnlyPageTable
) {
110 CetEnabled
= ((AsmReadCr4 () & CR4_CET_ENABLE
) != 0) ? TRUE
: FALSE
;
111 Cr0
.UintN
= AsmReadCr0 ();
112 WpEnabled
= (Cr0
.Bits
.WP
!= 0) ? TRUE
: FALSE
;
116 // CET must be disabled if WP is disabled. Disable CET before clearing CR0.WP.
122 AsmWriteCr0 (Cr0
.UintN
);
125 SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS
)(UINTN
)Buffer
, EFI_PAGES_TO_SIZE (PoolPages
), EFI_MEMORY_RO
);
127 Cr0
.UintN
= AsmReadCr0 ();
129 AsmWriteCr0 (Cr0
.UintN
);
144 This API provides a way to allocate memory for page table.
146 This API can be called more once to allocate memory for page tables.
148 Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
149 allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
150 is returned. If there is not enough memory remaining to satisfy the request, then NULL is
153 @param Pages The number of 4 KB pages to allocate.
155 @return A pointer to the allocated buffer or NULL if allocation fails.
159 AllocatePageTableMemory (
170 // Renew the pool if necessary.
172 if ((mPageTablePool
== NULL
) ||
173 (Pages
> mPageTablePool
->FreePages
))
175 if (!InitializePageTablePool (Pages
)) {
180 Buffer
= (UINT8
*)mPageTablePool
+ mPageTablePool
->Offset
;
182 mPageTablePool
->Offset
+= EFI_PAGES_TO_SIZE (Pages
);
183 mPageTablePool
->FreePages
-= Pages
;
189 Return length according to page attributes.
191 @param[in] PageAttributes The page attribute of the page entry.
193 @return The length of page entry.
196 PageAttributeToLength (
197 IN PAGE_ATTRIBUTE PageAttribute
202 for (Index
= 0; Index
< sizeof (mPageAttributeTable
)/sizeof (mPageAttributeTable
[0]); Index
++) {
203 if (PageAttribute
== mPageAttributeTable
[Index
].Attribute
) {
204 return (UINTN
)mPageAttributeTable
[Index
].Length
;
212 Return address mask according to page attributes.
214 @param[in] PageAttributes The page attribute of the page entry.
216 @return The address mask of page entry.
219 PageAttributeToMask (
220 IN PAGE_ATTRIBUTE PageAttribute
225 for (Index
= 0; Index
< sizeof (mPageAttributeTable
)/sizeof (mPageAttributeTable
[0]); Index
++) {
226 if (PageAttribute
== mPageAttributeTable
[Index
].Attribute
) {
227 return (UINTN
)mPageAttributeTable
[Index
].AddressMask
;
235 Return page table entry to match the address.
237 @param[in] PageTableBase The page table base.
238 @param[in] Enable5LevelPaging If PML5 paging is enabled.
239 @param[in] Address The address to be checked.
240 @param[out] PageAttributes The page attribute of the page entry.
242 @return The page entry.
246 IN UINTN PageTableBase
,
247 IN BOOLEAN Enable5LevelPaging
,
248 IN PHYSICAL_ADDRESS Address
,
249 OUT PAGE_ATTRIBUTE
*PageAttribute
263 Index5
= ((UINTN
)RShiftU64 (Address
, 48)) & PAGING_PAE_INDEX_MASK
;
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 if (sizeof (UINTN
) == sizeof (UINT64
)) {
270 if (Enable5LevelPaging
) {
271 L5PageTable
= (UINT64
*)PageTableBase
;
272 if (L5PageTable
[Index5
] == 0) {
273 *PageAttribute
= PageNone
;
277 L4PageTable
= (UINT64
*)(UINTN
)(L5PageTable
[Index5
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
279 L4PageTable
= (UINT64
*)PageTableBase
;
282 if (L4PageTable
[Index4
] == 0) {
283 *PageAttribute
= PageNone
;
287 L3PageTable
= (UINT64
*)(UINTN
)(L4PageTable
[Index4
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
289 L3PageTable
= (UINT64
*)PageTableBase
;
292 if (L3PageTable
[Index3
] == 0) {
293 *PageAttribute
= PageNone
;
297 if ((L3PageTable
[Index3
] & IA32_PG_PS
) != 0) {
299 *PageAttribute
= Page1G
;
300 return &L3PageTable
[Index3
];
303 L2PageTable
= (UINT64
*)(UINTN
)(L3PageTable
[Index3
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
304 if (L2PageTable
[Index2
] == 0) {
305 *PageAttribute
= PageNone
;
309 if ((L2PageTable
[Index2
] & IA32_PG_PS
) != 0) {
311 *PageAttribute
= Page2M
;
312 return &L2PageTable
[Index2
];
316 L1PageTable
= (UINT64
*)(UINTN
)(L2PageTable
[Index2
] & ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
);
317 if ((L1PageTable
[Index1
] == 0) && (Address
!= 0)) {
318 *PageAttribute
= PageNone
;
322 *PageAttribute
= Page4K
;
323 return &L1PageTable
[Index1
];
327 Return memory attributes of page entry.
329 @param[in] PageEntry The page entry.
331 @return Memory attributes of page entry.
334 GetAttributesFromPageEntry (
341 if ((*PageEntry
& IA32_PG_P
) == 0) {
342 Attributes
|= EFI_MEMORY_RP
;
345 if ((*PageEntry
& IA32_PG_RW
) == 0) {
346 Attributes
|= EFI_MEMORY_RO
;
349 if ((*PageEntry
& IA32_PG_NX
) != 0) {
350 Attributes
|= EFI_MEMORY_XP
;
357 Modify memory attributes of page entry.
359 @param[in] PageEntry The page entry.
360 @param[in] Attributes The bit mask of attributes to modify for the memory region.
361 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
362 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
365 ConvertPageEntryAttribute (
366 IN UINT64
*PageEntry
,
367 IN UINT64 Attributes
,
369 OUT BOOLEAN
*IsModified
372 UINT64 CurrentPageEntry
;
375 CurrentPageEntry
= *PageEntry
;
376 NewPageEntry
= CurrentPageEntry
;
377 if ((Attributes
& EFI_MEMORY_RP
) != 0) {
379 NewPageEntry
&= ~(UINT64
)IA32_PG_P
;
381 NewPageEntry
|= IA32_PG_P
;
385 if ((Attributes
& EFI_MEMORY_RO
) != 0) {
387 NewPageEntry
&= ~(UINT64
)IA32_PG_RW
;
388 if (mIsShadowStack
) {
390 // ReadOnly page need set Dirty bit for shadow stack
391 NewPageEntry
|= IA32_PG_D
;
392 // Clear user bit for supervisor shadow stack
393 NewPageEntry
&= ~(UINT64
)IA32_PG_U
;
396 // Clear dirty bit for non shadow stack, to protect RO page.
397 NewPageEntry
&= ~(UINT64
)IA32_PG_D
;
400 NewPageEntry
|= IA32_PG_RW
;
404 if ((Attributes
& EFI_MEMORY_XP
) != 0) {
407 NewPageEntry
|= IA32_PG_NX
;
409 NewPageEntry
&= ~IA32_PG_NX
;
414 *PageEntry
= NewPageEntry
;
415 if (CurrentPageEntry
!= NewPageEntry
) {
417 DEBUG ((DEBUG_VERBOSE
, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry
));
418 DEBUG ((DEBUG_VERBOSE
, "->0x%lx\n", NewPageEntry
));
425 This function returns if there is need to split page entry.
427 @param[in] BaseAddress The base address to be checked.
428 @param[in] Length The length to be checked.
429 @param[in] PageEntry The page entry to be checked.
430 @param[in] PageAttribute The page attribute of the page entry.
432 @retval SplitAttributes on if there is need to split page entry.
436 IN PHYSICAL_ADDRESS BaseAddress
,
438 IN UINT64
*PageEntry
,
439 IN PAGE_ATTRIBUTE PageAttribute
442 UINT64 PageEntryLength
;
444 PageEntryLength
= PageAttributeToLength (PageAttribute
);
446 if (((BaseAddress
& (PageEntryLength
- 1)) == 0) && (Length
>= PageEntryLength
)) {
450 if (((BaseAddress
& PAGING_2M_MASK
) != 0) || (Length
< SIZE_2MB
)) {
458 This function splits one page entry to small page entries.
460 @param[in] PageEntry The page entry to be splitted.
461 @param[in] PageAttribute The page attribute of the page entry.
462 @param[in] SplitAttribute How to split the page entry.
464 @retval RETURN_SUCCESS The page entry is splitted.
465 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
466 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
470 IN UINT64
*PageEntry
,
471 IN PAGE_ATTRIBUTE PageAttribute
,
472 IN PAGE_ATTRIBUTE SplitAttribute
476 UINT64
*NewPageEntry
;
479 ASSERT (PageAttribute
== Page2M
|| PageAttribute
== Page1G
);
481 if (PageAttribute
== Page2M
) {
485 ASSERT (SplitAttribute
== Page4K
);
486 if (SplitAttribute
== Page4K
) {
487 NewPageEntry
= AllocatePageTableMemory (1);
488 DEBUG ((DEBUG_VERBOSE
, "Split - 0x%x\n", NewPageEntry
));
489 if (NewPageEntry
== NULL
) {
490 return RETURN_OUT_OF_RESOURCES
;
493 BaseAddress
= *PageEntry
& PAGING_2M_ADDRESS_MASK_64
;
494 for (Index
= 0; Index
< SIZE_4KB
/ sizeof (UINT64
); Index
++) {
495 NewPageEntry
[Index
] = (BaseAddress
+ SIZE_4KB
* Index
) | mAddressEncMask
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
498 (*PageEntry
) = (UINT64
)(UINTN
)NewPageEntry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
499 return RETURN_SUCCESS
;
501 return RETURN_UNSUPPORTED
;
503 } else if (PageAttribute
== Page1G
) {
506 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
508 ASSERT (SplitAttribute
== Page2M
|| SplitAttribute
== Page4K
);
509 if (((SplitAttribute
== Page2M
) || (SplitAttribute
== Page4K
))) {
510 NewPageEntry
= AllocatePageTableMemory (1);
511 DEBUG ((DEBUG_VERBOSE
, "Split - 0x%x\n", NewPageEntry
));
512 if (NewPageEntry
== NULL
) {
513 return RETURN_OUT_OF_RESOURCES
;
516 BaseAddress
= *PageEntry
& PAGING_1G_ADDRESS_MASK_64
;
517 for (Index
= 0; Index
< SIZE_4KB
/ sizeof (UINT64
); Index
++) {
518 NewPageEntry
[Index
] = (BaseAddress
+ SIZE_2MB
* Index
) | mAddressEncMask
| IA32_PG_PS
| ((*PageEntry
) & PAGE_PROGATE_BITS
);
521 (*PageEntry
) = (UINT64
)(UINTN
)NewPageEntry
| mAddressEncMask
| PAGE_ATTRIBUTE_BITS
;
522 return RETURN_SUCCESS
;
524 return RETURN_UNSUPPORTED
;
527 return RETURN_UNSUPPORTED
;
532 This function modifies the page attributes for the memory region specified by BaseAddress and
533 Length from their current attributes to the attributes specified by Attributes.
535 Caller should make sure BaseAddress and Length is at page boundary.
537 @param[in] PageTableBase The page table base.
538 @param[in] EnablePML5Paging If PML5 paging is enabled.
539 @param[in] BaseAddress The physical address that is the start address of a memory region.
540 @param[in] Length The size in bytes of the memory region.
541 @param[in] Attributes The bit mask of attributes to modify for the memory region.
542 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
543 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
544 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
546 @retval RETURN_SUCCESS The attributes were modified for the memory region.
547 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
548 BaseAddress and Length cannot be modified.
549 @retval RETURN_INVALID_PARAMETER Length is zero.
550 Attributes specified an illegal combination of attributes that
551 cannot be set together.
552 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
553 the memory resource range.
554 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
555 resource range specified by BaseAddress and Length.
556 The bit mask of attributes is not support for the memory resource
557 range specified by BaseAddress and Length.
560 ConvertMemoryPageAttributes (
561 IN UINTN PageTableBase
,
562 IN BOOLEAN EnablePML5Paging
,
563 IN PHYSICAL_ADDRESS BaseAddress
,
565 IN UINT64 Attributes
,
567 OUT BOOLEAN
*IsSplitted OPTIONAL
,
568 OUT BOOLEAN
*IsModified OPTIONAL
572 PAGE_ATTRIBUTE PageAttribute
;
573 UINTN PageEntryLength
;
574 PAGE_ATTRIBUTE SplitAttribute
;
575 RETURN_STATUS Status
;
576 BOOLEAN IsEntryModified
;
577 EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress
;
579 ASSERT (Attributes
!= 0);
580 ASSERT ((Attributes
& ~EFI_MEMORY_ATTRIBUTE_MASK
) == 0);
582 ASSERT ((BaseAddress
& (SIZE_4KB
- 1)) == 0);
583 ASSERT ((Length
& (SIZE_4KB
- 1)) == 0);
586 return RETURN_INVALID_PARAMETER
;
589 MaximumSupportMemAddress
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)(LShiftU64 (1, mPhysicalAddressBits
) - 1);
590 if (BaseAddress
> MaximumSupportMemAddress
) {
591 return RETURN_UNSUPPORTED
;
594 if (Length
> MaximumSupportMemAddress
) {
595 return RETURN_UNSUPPORTED
;
598 if ((Length
!= 0) && (BaseAddress
> MaximumSupportMemAddress
- (Length
- 1))) {
599 return RETURN_UNSUPPORTED
;
602 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
604 if (IsSplitted
!= NULL
) {
608 if (IsModified
!= NULL
) {
613 // Below logic is to check 2M/4K page to make sure we do not waste memory.
615 while (Length
!= 0) {
616 PageEntry
= GetPageTableEntry (PageTableBase
, EnablePML5Paging
, BaseAddress
, &PageAttribute
);
617 if (PageEntry
== NULL
) {
618 return RETURN_UNSUPPORTED
;
621 PageEntryLength
= PageAttributeToLength (PageAttribute
);
622 SplitAttribute
= NeedSplitPage (BaseAddress
, Length
, PageEntry
, PageAttribute
);
623 if (SplitAttribute
== PageNone
) {
624 ConvertPageEntryAttribute (PageEntry
, Attributes
, IsSet
, &IsEntryModified
);
625 if (IsEntryModified
) {
626 if (IsModified
!= NULL
) {
632 // Convert success, move to next
634 BaseAddress
+= PageEntryLength
;
635 Length
-= PageEntryLength
;
637 Status
= SplitPage (PageEntry
, PageAttribute
, SplitAttribute
);
638 if (RETURN_ERROR (Status
)) {
639 return RETURN_UNSUPPORTED
;
642 if (IsSplitted
!= NULL
) {
646 if (IsModified
!= NULL
) {
651 // Just split current page
652 // Convert success in next around
657 return RETURN_SUCCESS
;
661 FlushTlb on current processor.
663 @param[in,out] Buffer Pointer to private data buffer.
667 FlushTlbOnCurrentProcessor (
675 FlushTlb for all processors.
684 FlushTlbOnCurrentProcessor (NULL
);
686 for (Index
= 0; Index
< gSmst
->NumberOfCpus
; Index
++) {
687 if (Index
!= gSmst
->CurrentlyExecutingCpu
) {
688 // Force to start up AP in blocking mode,
689 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor
, Index
, NULL
);
690 // Do not check return status, because AP might not be present in some corner cases.
696 This function sets the attributes for the memory region specified by BaseAddress and
697 Length from their current attributes to the attributes specified by Attributes.
699 @param[in] PageTableBase The page table base.
700 @param[in] EnablePML5Paging If PML5 paging is enabled.
701 @param[in] BaseAddress The physical address that is the start address of a memory region.
702 @param[in] Length The size in bytes of the memory region.
703 @param[in] Attributes The bit mask of attributes to set for the memory region.
704 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
706 @retval EFI_SUCCESS The attributes were set for the memory region.
707 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
708 BaseAddress and Length cannot be modified.
709 @retval EFI_INVALID_PARAMETER Length is zero.
710 Attributes specified an illegal combination of attributes that
711 cannot be set together.
712 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
713 the memory resource range.
714 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
715 resource range specified by BaseAddress and Length.
716 The bit mask of attributes is not support for the memory resource
717 range specified by BaseAddress and Length.
721 SmmSetMemoryAttributesEx (
722 IN UINTN PageTableBase
,
723 IN BOOLEAN EnablePML5Paging
,
724 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
726 IN UINT64 Attributes
,
727 OUT BOOLEAN
*IsSplitted OPTIONAL
733 Status
= ConvertMemoryPageAttributes (PageTableBase
, EnablePML5Paging
, BaseAddress
, Length
, Attributes
, TRUE
, IsSplitted
, &IsModified
);
734 if (!EFI_ERROR (Status
)) {
737 // Flush TLB as last step
747 This function clears the attributes for the memory region specified by BaseAddress and
748 Length from their current attributes to the attributes specified by Attributes.
750 @param[in] PageTableBase The page table base.
751 @param[in] EnablePML5Paging If PML5 paging is enabled.
752 @param[in] BaseAddress The physical address that is the start address of a memory region.
753 @param[in] Length The size in bytes of the memory region.
754 @param[in] Attributes The bit mask of attributes to clear for the memory region.
755 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
757 @retval EFI_SUCCESS The attributes were cleared for the memory region.
758 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
759 BaseAddress and Length cannot be modified.
760 @retval EFI_INVALID_PARAMETER Length is zero.
761 Attributes specified an illegal combination of attributes that
762 cannot be cleared together.
763 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
764 the memory resource range.
765 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
766 resource range specified by BaseAddress and Length.
767 The bit mask of attributes is not supported for the memory resource
768 range specified by BaseAddress and Length.
772 SmmClearMemoryAttributesEx (
773 IN UINTN PageTableBase
,
774 IN BOOLEAN EnablePML5Paging
,
775 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
777 IN UINT64 Attributes
,
778 OUT BOOLEAN
*IsSplitted OPTIONAL
784 Status
= ConvertMemoryPageAttributes (PageTableBase
, EnablePML5Paging
, BaseAddress
, Length
, Attributes
, FALSE
, IsSplitted
, &IsModified
);
785 if (!EFI_ERROR (Status
)) {
788 // Flush TLB as last step
798 This function sets the attributes for the memory region specified by BaseAddress and
799 Length from their current attributes to the attributes specified by Attributes.
801 @param[in] BaseAddress The physical address that is the start address of a memory region.
802 @param[in] Length The size in bytes of the memory region.
803 @param[in] Attributes The bit mask of attributes to set for the memory region.
805 @retval EFI_SUCCESS The attributes were set for the memory region.
806 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
807 BaseAddress and Length cannot be modified.
808 @retval EFI_INVALID_PARAMETER Length is zero.
809 Attributes specified an illegal combination of attributes that
810 cannot be set together.
811 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
812 the memory resource range.
813 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
814 resource range specified by BaseAddress and Length.
815 The bit mask of attributes is not supported for the memory resource
816 range specified by BaseAddress and Length.
820 SmmSetMemoryAttributes (
821 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
828 BOOLEAN Enable5LevelPaging
;
830 PageTableBase
= AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
;
831 Cr4
.UintN
= AsmReadCr4 ();
832 Enable5LevelPaging
= (BOOLEAN
)(Cr4
.Bits
.LA57
== 1);
833 return SmmSetMemoryAttributesEx (PageTableBase
, Enable5LevelPaging
, BaseAddress
, Length
, Attributes
, NULL
);
837 This function clears the attributes for the memory region specified by BaseAddress and
838 Length from their current attributes to the attributes specified by Attributes.
840 @param[in] BaseAddress The physical address that is the start address of a memory region.
841 @param[in] Length The size in bytes of the memory region.
842 @param[in] Attributes The bit mask of attributes to clear for the memory region.
844 @retval EFI_SUCCESS The attributes were cleared for the memory region.
845 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
846 BaseAddress and Length cannot be modified.
847 @retval EFI_INVALID_PARAMETER Length is zero.
848 Attributes specified an illegal combination of attributes that
849 cannot be cleared together.
850 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
851 the memory resource range.
852 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
853 resource range specified by BaseAddress and Length.
854 The bit mask of attributes is not supported for the memory resource
855 range specified by BaseAddress and Length.
859 SmmClearMemoryAttributes (
860 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
867 BOOLEAN Enable5LevelPaging
;
869 PageTableBase
= AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
;
870 Cr4
.UintN
= AsmReadCr4 ();
871 Enable5LevelPaging
= (BOOLEAN
)(Cr4
.Bits
.LA57
== 1);
872 return SmmClearMemoryAttributesEx (PageTableBase
, Enable5LevelPaging
, BaseAddress
, Length
, Attributes
, NULL
);
876 Set ShadowStack memory.
878 @param[in] Cr3 The page table base address.
879 @param[in] BaseAddress The physical address that is the start address of a memory region.
880 @param[in] Length The size in bytes of the memory region.
882 @retval EFI_SUCCESS The shadow stack memory is set.
887 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
893 mIsShadowStack
= TRUE
;
894 Status
= SmmSetMemoryAttributesEx (Cr3
, m5LevelPagingNeeded
, BaseAddress
, Length
, EFI_MEMORY_RO
, NULL
);
895 mIsShadowStack
= FALSE
;
901 Set not present memory.
903 @param[in] Cr3 The page table base address.
904 @param[in] BaseAddress The physical address that is the start address of a memory region.
905 @param[in] Length The size in bytes of the memory region.
907 @retval EFI_SUCCESS The not present memory is set.
912 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
918 Status
= SmmSetMemoryAttributesEx (Cr3
, m5LevelPagingNeeded
, BaseAddress
, Length
, EFI_MEMORY_RP
, NULL
);
923 Retrieves a pointer to the system configuration table from the SMM System Table
924 based on a specified GUID.
926 @param[in] TableGuid The pointer to table's GUID type.
927 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.
929 @retval EFI_SUCCESS A configuration table matching TableGuid was found.
930 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.
935 SmmGetSystemConfigurationTable (
936 IN EFI_GUID
*TableGuid
,
942 ASSERT (TableGuid
!= NULL
);
943 ASSERT (Table
!= NULL
);
946 for (Index
= 0; Index
< gSmst
->NumberOfTableEntries
; Index
++) {
947 if (CompareGuid (TableGuid
, &(gSmst
->SmmConfigurationTable
[Index
].VendorGuid
))) {
948 *Table
= gSmst
->SmmConfigurationTable
[Index
].VendorTable
;
953 return EFI_NOT_FOUND
;
957 This function sets SMM save state buffer to be RW and XP.
960 PatchSmmSaveStateMap (
969 TileCodeSize
= GetSmiHandlerSize ();
970 TileCodeSize
= ALIGN_VALUE (TileCodeSize
, SIZE_4KB
);
971 TileDataSize
= (SMRAM_SAVE_STATE_MAP_OFFSET
- SMM_PSD_OFFSET
) + sizeof (SMRAM_SAVE_STATE_MAP
);
972 TileDataSize
= ALIGN_VALUE (TileDataSize
, SIZE_4KB
);
973 TileSize
= TileDataSize
+ TileCodeSize
- 1;
974 TileSize
= 2 * GetPowerOfTwo32 ((UINT32
)TileSize
);
976 DEBUG ((DEBUG_INFO
, "PatchSmmSaveStateMap:\n"));
977 for (Index
= 0; Index
< mMaxNumberOfCpus
- 1; Index
++) {
981 SmmSetMemoryAttributes (
982 mCpuHotPlugData
.SmBase
[Index
] + SMM_HANDLER_OFFSET
,
986 SmmClearMemoryAttributes (
987 mCpuHotPlugData
.SmBase
[Index
] + SMM_HANDLER_OFFSET
,
995 SmmClearMemoryAttributes (
996 mCpuHotPlugData
.SmBase
[Index
] + SMM_HANDLER_OFFSET
+ TileCodeSize
,
997 TileSize
- TileCodeSize
,
1000 SmmSetMemoryAttributes (
1001 mCpuHotPlugData
.SmBase
[Index
] + SMM_HANDLER_OFFSET
+ TileCodeSize
,
1002 TileSize
- TileCodeSize
,
1010 SmmSetMemoryAttributes (
1011 mCpuHotPlugData
.SmBase
[mMaxNumberOfCpus
- 1] + SMM_HANDLER_OFFSET
,
1015 SmmClearMemoryAttributes (
1016 mCpuHotPlugData
.SmBase
[mMaxNumberOfCpus
- 1] + SMM_HANDLER_OFFSET
,
1024 SmmClearMemoryAttributes (
1025 mCpuHotPlugData
.SmBase
[mMaxNumberOfCpus
- 1] + SMM_HANDLER_OFFSET
+ TileCodeSize
,
1026 SIZE_32KB
- TileCodeSize
,
1029 SmmSetMemoryAttributes (
1030 mCpuHotPlugData
.SmBase
[mMaxNumberOfCpus
- 1] + SMM_HANDLER_OFFSET
+ TileCodeSize
,
1031 SIZE_32KB
- TileCodeSize
,
1037 This function sets GDT/IDT buffer to be RO and XP.
1044 EFI_PHYSICAL_ADDRESS BaseAddress
;
1050 DEBUG ((DEBUG_INFO
, "PatchGdtIdtMap - GDT:\n"));
1052 BaseAddress
= mGdtBuffer
;
1053 Size
= ALIGN_VALUE (mGdtBufferSize
, SIZE_4KB
);
1055 // The range should have been set to RO
1056 // if it is allocated with EfiRuntimeServicesCode.
1058 SmmSetMemoryAttributes (
1067 DEBUG ((DEBUG_INFO
, "PatchGdtIdtMap - IDT:\n"));
1069 BaseAddress
= gcSmiIdtr
.Base
;
1070 Size
= ALIGN_VALUE (gcSmiIdtr
.Limit
+ 1, SIZE_4KB
);
1072 // The range should have been set to RO
1073 // if it is allocated with EfiRuntimeServicesCode.
1075 SmmSetMemoryAttributes (
1083 This function sets memory attribute according to MemoryAttributesTable.
1086 SetMemMapAttributes (
1090 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
1091 EFI_MEMORY_DESCRIPTOR
*MemoryMapStart
;
1092 UINTN MemoryMapEntryCount
;
1093 UINTN DescriptorSize
;
1095 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE
*MemoryAttributesTable
;
1097 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid
, (VOID
**)&MemoryAttributesTable
);
1098 if (MemoryAttributesTable
== NULL
) {
1099 DEBUG ((DEBUG_INFO
, "MemoryAttributesTable - NULL\n"));
1103 DEBUG ((DEBUG_INFO
, "MemoryAttributesTable:\n"));
1104 DEBUG ((DEBUG_INFO
, " Version - 0x%08x\n", MemoryAttributesTable
->Version
));
1105 DEBUG ((DEBUG_INFO
, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable
->NumberOfEntries
));
1106 DEBUG ((DEBUG_INFO
, " DescriptorSize - 0x%08x\n", MemoryAttributesTable
->DescriptorSize
));
1108 MemoryMapEntryCount
= MemoryAttributesTable
->NumberOfEntries
;
1109 DescriptorSize
= MemoryAttributesTable
->DescriptorSize
;
1110 MemoryMapStart
= (EFI_MEMORY_DESCRIPTOR
*)(MemoryAttributesTable
+ 1);
1111 MemoryMap
= MemoryMapStart
;
1112 for (Index
= 0; Index
< MemoryMapEntryCount
; Index
++) {
1113 DEBUG ((DEBUG_INFO
, "Entry (0x%x)\n", MemoryMap
));
1114 DEBUG ((DEBUG_INFO
, " Type - 0x%x\n", MemoryMap
->Type
));
1115 DEBUG ((DEBUG_INFO
, " PhysicalStart - 0x%016lx\n", MemoryMap
->PhysicalStart
));
1116 DEBUG ((DEBUG_INFO
, " VirtualStart - 0x%016lx\n", MemoryMap
->VirtualStart
));
1117 DEBUG ((DEBUG_INFO
, " NumberOfPages - 0x%016lx\n", MemoryMap
->NumberOfPages
));
1118 DEBUG ((DEBUG_INFO
, " Attribute - 0x%016lx\n", MemoryMap
->Attribute
));
1119 MemoryMap
= NEXT_MEMORY_DESCRIPTOR (MemoryMap
, DescriptorSize
);
1122 MemoryMap
= MemoryMapStart
;
1123 for (Index
= 0; Index
< MemoryMapEntryCount
; Index
++) {
1124 DEBUG ((DEBUG_VERBOSE
, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap
->PhysicalStart
, MemoryMap
->NumberOfPages
));
1125 switch (MemoryMap
->Type
) {
1126 case EfiRuntimeServicesCode
:
1127 SmmSetMemoryAttributes (
1128 MemoryMap
->PhysicalStart
,
1129 EFI_PAGES_TO_SIZE ((UINTN
)MemoryMap
->NumberOfPages
),
1133 case EfiRuntimeServicesData
:
1134 SmmSetMemoryAttributes (
1135 MemoryMap
->PhysicalStart
,
1136 EFI_PAGES_TO_SIZE ((UINTN
)MemoryMap
->NumberOfPages
),
1141 SmmSetMemoryAttributes (
1142 MemoryMap
->PhysicalStart
,
1143 EFI_PAGES_TO_SIZE ((UINTN
)MemoryMap
->NumberOfPages
),
1149 MemoryMap
= NEXT_MEMORY_DESCRIPTOR (MemoryMap
, DescriptorSize
);
1152 PatchSmmSaveStateMap ();
1159 Sort memory map entries based upon PhysicalStart, from low to high.
1161 @param MemoryMap A pointer to the buffer in which firmware places
1162 the current memory map.
1163 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
1164 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
1169 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
1170 IN UINTN MemoryMapSize
,
1171 IN UINTN DescriptorSize
1174 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
1175 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
1176 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
1177 EFI_MEMORY_DESCRIPTOR TempMemoryMap
;
1179 MemoryMapEntry
= MemoryMap
;
1180 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
1181 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*)((UINT8
*)MemoryMap
+ MemoryMapSize
);
1182 while (MemoryMapEntry
< MemoryMapEnd
) {
1183 while (NextMemoryMapEntry
< MemoryMapEnd
) {
1184 if (MemoryMapEntry
->PhysicalStart
> NextMemoryMapEntry
->PhysicalStart
) {
1185 CopyMem (&TempMemoryMap
, MemoryMapEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
1186 CopyMem (MemoryMapEntry
, NextMemoryMapEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
1187 CopyMem (NextMemoryMapEntry
, &TempMemoryMap
, sizeof (EFI_MEMORY_DESCRIPTOR
));
1190 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
1193 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
1194 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
1199 Return if a UEFI memory page should be marked as not present in SMM page table.
1200 If the memory map entries type is
1201 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1202 EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.
1205 @param[in] MemoryMap A pointer to the memory descriptor.
1207 @return TRUE The memory described will be marked as not present in SMM page table.
1208 @return FALSE The memory described will not be marked as not present in SMM page table.
1211 IsUefiPageNotPresent (
1212 IN EFI_MEMORY_DESCRIPTOR
*MemoryMap
1215 switch (MemoryMap
->Type
) {
1218 case EfiBootServicesCode
:
1219 case EfiBootServicesData
:
1220 case EfiConventionalMemory
:
1221 case EfiUnusableMemory
:
1222 case EfiACPIReclaimMemory
:
1230 Merge continuous memory map entries whose type is
1231 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1232 EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by
1233 these entries will be set as NOT present in SMM page table.
1235 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
1236 the current memory map.
1237 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
1238 MemoryMap buffer. On input, this is the size of
1239 the current memory map. On output,
1240 it is the size of new memory map after merge.
1241 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
1245 MergeMemoryMapForNotPresentEntry (
1246 IN OUT EFI_MEMORY_DESCRIPTOR
*MemoryMap
,
1247 IN OUT UINTN
*MemoryMapSize
,
1248 IN UINTN DescriptorSize
1251 EFI_MEMORY_DESCRIPTOR
*MemoryMapEntry
;
1252 EFI_MEMORY_DESCRIPTOR
*MemoryMapEnd
;
1253 UINT64 MemoryBlockLength
;
1254 EFI_MEMORY_DESCRIPTOR
*NewMemoryMapEntry
;
1255 EFI_MEMORY_DESCRIPTOR
*NextMemoryMapEntry
;
1257 MemoryMapEntry
= MemoryMap
;
1258 NewMemoryMapEntry
= MemoryMap
;
1259 MemoryMapEnd
= (EFI_MEMORY_DESCRIPTOR
*)((UINT8
*)MemoryMap
+ *MemoryMapSize
);
1260 while ((UINTN
)MemoryMapEntry
< (UINTN
)MemoryMapEnd
) {
1261 CopyMem (NewMemoryMapEntry
, MemoryMapEntry
, sizeof (EFI_MEMORY_DESCRIPTOR
));
1262 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
1265 MemoryBlockLength
= (UINT64
)(EFI_PAGES_TO_SIZE ((UINTN
)MemoryMapEntry
->NumberOfPages
));
1266 if (((UINTN
)NextMemoryMapEntry
< (UINTN
)MemoryMapEnd
) &&
1267 IsUefiPageNotPresent (MemoryMapEntry
) && IsUefiPageNotPresent (NextMemoryMapEntry
) &&
1268 ((MemoryMapEntry
->PhysicalStart
+ MemoryBlockLength
) == NextMemoryMapEntry
->PhysicalStart
))
1270 MemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
1271 if (NewMemoryMapEntry
!= MemoryMapEntry
) {
1272 NewMemoryMapEntry
->NumberOfPages
+= NextMemoryMapEntry
->NumberOfPages
;
1275 NextMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
1278 MemoryMapEntry
= PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry
, DescriptorSize
);
1283 MemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry
, DescriptorSize
);
1284 NewMemoryMapEntry
= NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry
, DescriptorSize
);
1287 *MemoryMapSize
= (UINTN
)NewMemoryMapEntry
- (UINTN
)MemoryMap
;
1293 This function caches the GCD memory map information.
1300 UINTN NumberOfDescriptors
;
1301 EFI_GCD_MEMORY_SPACE_DESCRIPTOR
*MemSpaceMap
;
1305 Status
= gDS
->GetMemorySpaceMap (&NumberOfDescriptors
, &MemSpaceMap
);
1306 if (EFI_ERROR (Status
)) {
1310 mGcdMemNumberOfDesc
= 0;
1311 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
1312 if ((MemSpaceMap
[Index
].GcdMemoryType
== EfiGcdMemoryTypeReserved
) &&
1313 ((MemSpaceMap
[Index
].Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
1314 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
))
1317 mGcdMemNumberOfDesc
++;
1321 mGcdMemSpace
= AllocateZeroPool (mGcdMemNumberOfDesc
* sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR
));
1322 ASSERT (mGcdMemSpace
!= NULL
);
1323 if (mGcdMemSpace
== NULL
) {
1324 mGcdMemNumberOfDesc
= 0;
1325 gBS
->FreePool (MemSpaceMap
);
1329 mGcdMemNumberOfDesc
= 0;
1330 for (Index
= 0; Index
< NumberOfDescriptors
; Index
++) {
1331 if ((MemSpaceMap
[Index
].GcdMemoryType
== EfiGcdMemoryTypeReserved
) &&
1332 ((MemSpaceMap
[Index
].Capabilities
& (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
| EFI_MEMORY_TESTED
)) ==
1333 (EFI_MEMORY_PRESENT
| EFI_MEMORY_INITIALIZED
))
1337 &mGcdMemSpace
[mGcdMemNumberOfDesc
],
1338 &MemSpaceMap
[Index
],
1339 sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR
)
1341 mGcdMemNumberOfDesc
++;
1345 gBS
->FreePool (MemSpaceMap
);
1349 Get UEFI MemoryAttributesTable.
1352 GetUefiMemoryAttributesTable (
1357 EFI_MEMORY_ATTRIBUTES_TABLE
*MemoryAttributesTable
;
1358 UINTN MemoryAttributesTableSize
;
1360 Status
= EfiGetSystemConfigurationTable (&gEfiMemoryAttributesTableGuid
, (VOID
**)&MemoryAttributesTable
);
1361 if (!EFI_ERROR (Status
) && (MemoryAttributesTable
!= NULL
)) {
1362 MemoryAttributesTableSize
= sizeof (EFI_MEMORY_ATTRIBUTES_TABLE
) + MemoryAttributesTable
->DescriptorSize
* MemoryAttributesTable
->NumberOfEntries
;
1363 mUefiMemoryAttributesTable
= AllocateCopyPool (MemoryAttributesTableSize
, MemoryAttributesTable
);
1364 ASSERT (mUefiMemoryAttributesTable
!= NULL
);
1369 This function caches the UEFI memory map information.
1378 UINT32 DescriptorVersion
;
1379 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
1380 UINTN UefiMemoryMapSize
;
1382 DEBUG ((DEBUG_INFO
, "GetUefiMemoryMap\n"));
1384 UefiMemoryMapSize
= 0;
1386 Status
= gBS
->GetMemoryMap (
1390 &mUefiDescriptorSize
,
1393 ASSERT (Status
== EFI_BUFFER_TOO_SMALL
);
1396 Status
= gBS
->AllocatePool (EfiBootServicesData
, UefiMemoryMapSize
, (VOID
**)&MemoryMap
);
1397 ASSERT (MemoryMap
!= NULL
);
1398 if (MemoryMap
== NULL
) {
1402 Status
= gBS
->GetMemoryMap (
1406 &mUefiDescriptorSize
,
1409 if (EFI_ERROR (Status
)) {
1410 gBS
->FreePool (MemoryMap
);
1413 } while (Status
== EFI_BUFFER_TOO_SMALL
);
1415 if (MemoryMap
== NULL
) {
1419 SortMemoryMap (MemoryMap
, UefiMemoryMapSize
, mUefiDescriptorSize
);
1420 MergeMemoryMapForNotPresentEntry (MemoryMap
, &UefiMemoryMapSize
, mUefiDescriptorSize
);
1422 mUefiMemoryMapSize
= UefiMemoryMapSize
;
1423 mUefiMemoryMap
= AllocateCopyPool (UefiMemoryMapSize
, MemoryMap
);
1424 ASSERT (mUefiMemoryMap
!= NULL
);
1426 gBS
->FreePool (MemoryMap
);
1429 // Get additional information from GCD memory map.
1434 // Get UEFI memory attributes table.
1436 GetUefiMemoryAttributesTable ();
1440 This function sets UEFI memory attribute according to UEFI memory map.
1442 The normal memory region is marked as not present, such as
1443 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1444 EfiUnusableMemory, EfiACPIReclaimMemory.
1447 SetUefiMemMapAttributes (
1452 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
1453 UINTN MemoryMapEntryCount
;
1455 EFI_MEMORY_DESCRIPTOR
*Entry
;
1457 DEBUG ((DEBUG_INFO
, "SetUefiMemMapAttributes\n"));
1459 if (mUefiMemoryMap
!= NULL
) {
1460 MemoryMapEntryCount
= mUefiMemoryMapSize
/mUefiDescriptorSize
;
1461 MemoryMap
= mUefiMemoryMap
;
1462 for (Index
= 0; Index
< MemoryMapEntryCount
; Index
++) {
1463 if (IsUefiPageNotPresent (MemoryMap
)) {
1464 Status
= SmmSetMemoryAttributes (
1465 MemoryMap
->PhysicalStart
,
1466 EFI_PAGES_TO_SIZE ((UINTN
)MemoryMap
->NumberOfPages
),
1471 "UefiMemory protection: 0x%lx - 0x%lx %r\n",
1472 MemoryMap
->PhysicalStart
,
1473 MemoryMap
->PhysicalStart
+ (UINT64
)EFI_PAGES_TO_SIZE ((UINTN
)MemoryMap
->NumberOfPages
),
1478 MemoryMap
= NEXT_MEMORY_DESCRIPTOR (MemoryMap
, mUefiDescriptorSize
);
1483 // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
1487 // Set untested memory as not present.
1489 if (mGcdMemSpace
!= NULL
) {
1490 for (Index
= 0; Index
< mGcdMemNumberOfDesc
; Index
++) {
1491 Status
= SmmSetMemoryAttributes (
1492 mGcdMemSpace
[Index
].BaseAddress
,
1493 mGcdMemSpace
[Index
].Length
,
1498 "GcdMemory protection: 0x%lx - 0x%lx %r\n",
1499 mGcdMemSpace
[Index
].BaseAddress
,
1500 mGcdMemSpace
[Index
].BaseAddress
+ mGcdMemSpace
[Index
].Length
,
1507 // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().
1511 // Set UEFI runtime memory with EFI_MEMORY_RO as not present.
1513 if (mUefiMemoryAttributesTable
!= NULL
) {
1514 Entry
= (EFI_MEMORY_DESCRIPTOR
*)(mUefiMemoryAttributesTable
+ 1);
1515 for (Index
= 0; Index
< mUefiMemoryAttributesTable
->NumberOfEntries
; Index
++) {
1516 if ((Entry
->Type
== EfiRuntimeServicesCode
) || (Entry
->Type
== EfiRuntimeServicesData
)) {
1517 if ((Entry
->Attribute
& EFI_MEMORY_RO
) != 0) {
1518 Status
= SmmSetMemoryAttributes (
1519 Entry
->PhysicalStart
,
1520 EFI_PAGES_TO_SIZE ((UINTN
)Entry
->NumberOfPages
),
1525 "UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",
1526 Entry
->PhysicalStart
,
1527 Entry
->PhysicalStart
+ (UINT64
)EFI_PAGES_TO_SIZE ((UINTN
)Entry
->NumberOfPages
),
1533 Entry
= NEXT_MEMORY_DESCRIPTOR (Entry
, mUefiMemoryAttributesTable
->DescriptorSize
);
1538 // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
1543 Return if the Address is forbidden as SMM communication buffer.
1545 @param[in] Address the address to be checked
1547 @return TRUE The address is forbidden as SMM communication buffer.
1548 @return FALSE The address is allowed as SMM communication buffer.
1551 IsSmmCommBufferForbiddenAddress (
1555 EFI_MEMORY_DESCRIPTOR
*MemoryMap
;
1556 UINTN MemoryMapEntryCount
;
1558 EFI_MEMORY_DESCRIPTOR
*Entry
;
1560 if (mUefiMemoryMap
!= NULL
) {
1561 MemoryMap
= mUefiMemoryMap
;
1562 MemoryMapEntryCount
= mUefiMemoryMapSize
/mUefiDescriptorSize
;
1563 for (Index
= 0; Index
< MemoryMapEntryCount
; Index
++) {
1564 if (IsUefiPageNotPresent (MemoryMap
)) {
1565 if ((Address
>= MemoryMap
->PhysicalStart
) &&
1566 (Address
< MemoryMap
->PhysicalStart
+ EFI_PAGES_TO_SIZE ((UINTN
)MemoryMap
->NumberOfPages
)))
1572 MemoryMap
= NEXT_MEMORY_DESCRIPTOR (MemoryMap
, mUefiDescriptorSize
);
1576 if (mGcdMemSpace
!= NULL
) {
1577 for (Index
= 0; Index
< mGcdMemNumberOfDesc
; Index
++) {
1578 if ((Address
>= mGcdMemSpace
[Index
].BaseAddress
) &&
1579 (Address
< mGcdMemSpace
[Index
].BaseAddress
+ mGcdMemSpace
[Index
].Length
))
1586 if (mUefiMemoryAttributesTable
!= NULL
) {
1587 Entry
= (EFI_MEMORY_DESCRIPTOR
*)(mUefiMemoryAttributesTable
+ 1);
1588 for (Index
= 0; Index
< mUefiMemoryAttributesTable
->NumberOfEntries
; Index
++) {
1589 if ((Entry
->Type
== EfiRuntimeServicesCode
) || (Entry
->Type
== EfiRuntimeServicesData
)) {
1590 if ((Entry
->Attribute
& EFI_MEMORY_RO
) != 0) {
1591 if ((Address
>= Entry
->PhysicalStart
) &&
1592 (Address
< Entry
->PhysicalStart
+ LShiftU64 (Entry
->NumberOfPages
, EFI_PAGE_SHIFT
)))
1597 Entry
= NEXT_MEMORY_DESCRIPTOR (Entry
, mUefiMemoryAttributesTable
->DescriptorSize
);
1607 This function set given attributes of the memory region specified by
1608 BaseAddress and Length.
1610 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1611 @param BaseAddress The physical address that is the start address of
1613 @param Length The size in bytes of the memory region.
1614 @param Attributes The bit mask of attributes to set for the memory
1617 @retval EFI_SUCCESS The attributes were set for the memory region.
1618 @retval EFI_INVALID_PARAMETER Length is zero.
1619 Attributes specified an illegal combination of
1620 attributes that cannot be set together.
1621 @retval EFI_UNSUPPORTED The processor does not support one or more
1622 bytes of the memory resource range specified
1623 by BaseAddress and Length.
1624 The bit mask of attributes is not supported for
1625 the memory resource range specified by
1626 BaseAddress and Length.
1631 EdkiiSmmSetMemoryAttributes (
1632 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL
*This
,
1633 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
1635 IN UINT64 Attributes
1638 return SmmSetMemoryAttributes (BaseAddress
, Length
, Attributes
);
1642 This function clears given attributes of the memory region specified by
1643 BaseAddress and Length.
1645 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1646 @param BaseAddress The physical address that is the start address of
1648 @param Length The size in bytes of the memory region.
1649 @param Attributes The bit mask of attributes to clear for the memory
1652 @retval EFI_SUCCESS The attributes were cleared for the memory region.
1653 @retval EFI_INVALID_PARAMETER Length is zero.
1654 Attributes specified an illegal combination of
1655 attributes that cannot be cleared together.
1656 @retval EFI_UNSUPPORTED The processor does not support one or more
1657 bytes of the memory resource range specified
1658 by BaseAddress and Length.
1659 The bit mask of attributes is not supported for
1660 the memory resource range specified by
1661 BaseAddress and Length.
1666 EdkiiSmmClearMemoryAttributes (
1667 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL
*This
,
1668 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
1670 IN UINT64 Attributes
1673 return SmmClearMemoryAttributes (BaseAddress
, Length
, Attributes
);
1677 This function retrieves the attributes of the memory region specified by
1678 BaseAddress and Length. If different attributes are got from different part
1679 of the memory region, EFI_NO_MAPPING will be returned.
1681 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1682 @param BaseAddress The physical address that is the start address of
1684 @param Length The size in bytes of the memory region.
1685 @param Attributes Pointer to attributes returned.
1687 @retval EFI_SUCCESS The attributes got for the memory region.
1688 @retval EFI_INVALID_PARAMETER Length is zero.
1690 @retval EFI_NO_MAPPING Attributes are not consistent cross the memory
1692 @retval EFI_UNSUPPORTED The processor does not support one or more
1693 bytes of the memory resource range specified
1694 by BaseAddress and Length.
1699 EdkiiSmmGetMemoryAttributes (
1700 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL
*This
,
1701 IN EFI_PHYSICAL_ADDRESS BaseAddress
,
1703 OUT UINT64
*Attributes
1706 EFI_PHYSICAL_ADDRESS Address
;
1709 PAGE_ATTRIBUTE PageAttr
;
1711 UINTN PageTableBase
;
1712 BOOLEAN EnablePML5Paging
;
1715 if ((Length
< SIZE_4KB
) || (Attributes
== NULL
)) {
1716 return EFI_INVALID_PARAMETER
;
1719 Size
= (INT64
)Length
;
1720 MemAttr
= (UINT64
)-1;
1722 PageTableBase
= AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
;
1723 Cr4
.UintN
= AsmReadCr4 ();
1724 EnablePML5Paging
= (BOOLEAN
)(Cr4
.Bits
.LA57
== 1);
1727 PageEntry
= GetPageTableEntry (PageTableBase
, EnablePML5Paging
, BaseAddress
, &PageAttr
);
1728 if ((PageEntry
== NULL
) || (PageAttr
== PageNone
)) {
1729 return EFI_UNSUPPORTED
;
1733 // If the memory range is cross page table boundary, make sure they
1734 // share the same attribute. Return EFI_NO_MAPPING if not.
1736 *Attributes
= GetAttributesFromPageEntry (PageEntry
);
1737 if ((MemAttr
!= (UINT64
)-1) && (*Attributes
!= MemAttr
)) {
1738 return EFI_NO_MAPPING
;
1743 Address
= *PageEntry
& ~mAddressEncMask
& PAGING_4K_ADDRESS_MASK_64
;
1744 Size
-= (SIZE_4KB
- (BaseAddress
- Address
));
1745 BaseAddress
+= (SIZE_4KB
- (BaseAddress
- Address
));
1749 Address
= *PageEntry
& ~mAddressEncMask
& PAGING_2M_ADDRESS_MASK_64
;
1750 Size
-= SIZE_2MB
- (BaseAddress
- Address
);
1751 BaseAddress
+= SIZE_2MB
- (BaseAddress
- Address
);
1755 Address
= *PageEntry
& ~mAddressEncMask
& PAGING_1G_ADDRESS_MASK_64
;
1756 Size
-= SIZE_1GB
- (BaseAddress
- Address
);
1757 BaseAddress
+= SIZE_1GB
- (BaseAddress
- Address
);
1761 return EFI_UNSUPPORTED
;
1764 MemAttr
= *Attributes
;
1771 Prevent the memory pages used for SMM page table from been overwritten.
1774 EnablePageTableProtection (
1778 PAGE_TABLE_POOL
*HeadPool
;
1779 PAGE_TABLE_POOL
*Pool
;
1781 EFI_PHYSICAL_ADDRESS Address
;
1782 UINTN PageTableBase
;
1784 if (mPageTablePool
== NULL
) {
1788 PageTableBase
= AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64
;
1791 // ConvertMemoryPageAttributes might update mPageTablePool. It's safer to
1792 // remember original one in advance.
1794 HeadPool
= mPageTablePool
;
1797 Address
= (EFI_PHYSICAL_ADDRESS
)(UINTN
)Pool
;
1798 PoolSize
= Pool
->Offset
+ EFI_PAGES_TO_SIZE (Pool
->FreePages
);
1800 // Set entire pool including header, used-memory and left free-memory as ReadOnly in SMM page table.
1802 ConvertMemoryPageAttributes (PageTableBase
, m5LevelPagingNeeded
, Address
, PoolSize
, EFI_MEMORY_RO
, TRUE
, NULL
, NULL
);
1803 Pool
= Pool
->NextPool
;
1804 } while (Pool
!= HeadPool
);
1808 Return whether memory used by SMM page table need to be set as Read Only.
1810 @retval TRUE Need to set SMM page table as Read Only.
1811 @retval FALSE Do not set SMM page table as Read Only.
1814 IfReadOnlyPageTableNeeded (
1819 // Don't mark page table memory as read-only if
1820 // - no restriction on access to non-SMRAM memory; or
1821 // - SMM heap guard feature enabled; or
1822 // BIT2: SMM page guard enabled
1823 // BIT3: SMM pool guard enabled
1824 // - SMM profile feature enabled
1826 if (!IsRestrictedMemoryAccess () ||
1827 ((PcdGet8 (PcdHeapGuardPropertyMask
) & (BIT3
| BIT2
)) != 0) ||
1828 FeaturePcdGet (PcdCpuSmmProfileEnable
))
1830 if (sizeof (UINTN
) == sizeof (UINT64
)) {
1832 // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time.
1835 !(IsRestrictedMemoryAccess () &&
1836 (PcdGet8 (PcdHeapGuardPropertyMask
) & (BIT3
| BIT2
)) != 0)
1840 // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time.
1842 ASSERT (!(IsRestrictedMemoryAccess () && FeaturePcdGet (PcdCpuSmmProfileEnable
)));
1852 This function sets memory attribute for page table.
1855 SetPageTableAttributes (
1861 if (!IfReadOnlyPageTableNeeded ()) {
1865 DEBUG ((DEBUG_INFO
, "SetPageTableAttributes\n"));
1868 // Disable write protection, because we need mark page table to be write protected.
1869 // We need *write* page table memory, to mark itself to be *read only*.
1871 CetEnabled
= ((AsmReadCr4 () & CR4_CET_ENABLE
) != 0) ? TRUE
: FALSE
;
1874 // CET must be disabled if WP is disabled.
1879 AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP
);
1881 // Set memory used by page table as Read Only.
1882 DEBUG ((DEBUG_INFO
, "Start...\n"));
1883 EnablePageTableProtection ();
1886 // Enable write protection, after page table attribute updated.
1888 AsmWriteCr0 (AsmReadCr0 () | CR0_WP
);
1889 mIsReadOnlyPageTable
= TRUE
;
1892 // Flush TLB after mark all page table pool as read only.