]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / UefiCpuPkg / PiSmmCpuDxeSmm / SmmCpuMemoryManagement.c
1 /** @file
2
3 Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>
4 SPDX-License-Identifier: BSD-2-Clause-Patent
5
6 **/
7
8 #include "PiSmmCpuDxeSmm.h"
9
10 //
11 // attributes for reserved memory before it is promoted to system memory
12 //
13 #define EFI_MEMORY_PRESENT 0x0100000000000000ULL
14 #define EFI_MEMORY_INITIALIZED 0x0200000000000000ULL
15 #define EFI_MEMORY_TESTED 0x0400000000000000ULL
16
17 #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
18 ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size)))
19
20 EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;
21 UINTN mUefiMemoryMapSize;
22 UINTN mUefiDescriptorSize;
23
24 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mGcdMemSpace = NULL;
25 UINTN mGcdMemNumberOfDesc = 0;
26
27 EFI_MEMORY_ATTRIBUTES_TABLE *mUefiMemoryAttributesTable = NULL;
28
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 },
33 };
34
35 BOOLEAN mIsShadowStack = FALSE;
36 BOOLEAN m5LevelPagingNeeded = FALSE;
37
38 //
39 // Global variable to keep track current available memory used as page table.
40 //
41 PAGE_TABLE_POOL *mPageTablePool = NULL;
42
43 //
44 // If memory used by SMM page table has been mareked as ReadOnly.
45 //
46 BOOLEAN mIsReadOnlyPageTable = FALSE;
47
48 /**
49 Initialize a buffer pool for page table use only.
50
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.
55
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
58 happen in practice.
59
60 @param PoolPages The least page number of the pool to be created.
61
62 @retval TRUE The pool is initialized successfully.
63 @retval FALSE The memory is out of resource.
64 **/
65 BOOLEAN
66 InitializePageTablePool (
67 IN UINTN PoolPages
68 )
69 {
70 VOID *Buffer;
71 BOOLEAN CetEnabled;
72 BOOLEAN WpEnabled;
73 IA32_CR0 Cr0;
74
75 //
76 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
77 // header.
78 //
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);
83 if (Buffer == NULL) {
84 DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
85 return FALSE;
86 }
87
88 //
89 // Link all pools into a list for easier track later.
90 //
91 if (mPageTablePool == NULL) {
92 mPageTablePool = Buffer;
93 mPageTablePool->NextPool = mPageTablePool;
94 } else {
95 ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
96 mPageTablePool->NextPool = Buffer;
97 mPageTablePool = Buffer;
98 }
99
100 //
101 // Reserve one page for pool header.
102 //
103 mPageTablePool->FreePages = PoolPages - 1;
104 mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
105
106 //
107 // If page table memory has been marked as RO, mark the new pool pages as read-only.
108 //
109 if (mIsReadOnlyPageTable) {
110 CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;
111 Cr0.UintN = AsmReadCr0 ();
112 WpEnabled = (Cr0.Bits.WP != 0) ? TRUE : FALSE;
113 if (WpEnabled) {
114 if (CetEnabled) {
115 //
116 // CET must be disabled if WP is disabled. Disable CET before clearing CR0.WP.
117 //
118 DisableCet ();
119 }
120
121 Cr0.Bits.WP = 0;
122 AsmWriteCr0 (Cr0.UintN);
123 }
124
125 SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (PoolPages), EFI_MEMORY_RO);
126 if (WpEnabled) {
127 Cr0.UintN = AsmReadCr0 ();
128 Cr0.Bits.WP = 1;
129 AsmWriteCr0 (Cr0.UintN);
130
131 if (CetEnabled) {
132 //
133 // re-enable CET.
134 //
135 EnableCet ();
136 }
137 }
138 }
139
140 return TRUE;
141 }
142
143 /**
144 This API provides a way to allocate memory for page table.
145
146 This API can be called more once to allocate memory for page tables.
147
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
151 returned.
152
153 @param Pages The number of 4 KB pages to allocate.
154
155 @return A pointer to the allocated buffer or NULL if allocation fails.
156
157 **/
158 VOID *
159 AllocatePageTableMemory (
160 IN UINTN Pages
161 )
162 {
163 VOID *Buffer;
164
165 if (Pages == 0) {
166 return NULL;
167 }
168
169 //
170 // Renew the pool if necessary.
171 //
172 if ((mPageTablePool == NULL) ||
173 (Pages > mPageTablePool->FreePages))
174 {
175 if (!InitializePageTablePool (Pages)) {
176 return NULL;
177 }
178 }
179
180 Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
181
182 mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
183 mPageTablePool->FreePages -= Pages;
184
185 return Buffer;
186 }
187
188 /**
189 Return length according to page attributes.
190
191 @param[in] PageAttributes The page attribute of the page entry.
192
193 @return The length of page entry.
194 **/
195 UINTN
196 PageAttributeToLength (
197 IN PAGE_ATTRIBUTE PageAttribute
198 )
199 {
200 UINTN Index;
201
202 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {
203 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
204 return (UINTN)mPageAttributeTable[Index].Length;
205 }
206 }
207
208 return 0;
209 }
210
211 /**
212 Return address mask according to page attributes.
213
214 @param[in] PageAttributes The page attribute of the page entry.
215
216 @return The address mask of page entry.
217 **/
218 UINTN
219 PageAttributeToMask (
220 IN PAGE_ATTRIBUTE PageAttribute
221 )
222 {
223 UINTN Index;
224
225 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {
226 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
227 return (UINTN)mPageAttributeTable[Index].AddressMask;
228 }
229 }
230
231 return 0;
232 }
233
234 /**
235 Return page table entry to match the address.
236
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.
241
242 @return The page entry.
243 **/
244 VOID *
245 GetPageTableEntry (
246 IN UINTN PageTableBase,
247 IN BOOLEAN Enable5LevelPaging,
248 IN PHYSICAL_ADDRESS Address,
249 OUT PAGE_ATTRIBUTE *PageAttribute
250 )
251 {
252 UINTN Index1;
253 UINTN Index2;
254 UINTN Index3;
255 UINTN Index4;
256 UINTN Index5;
257 UINT64 *L1PageTable;
258 UINT64 *L2PageTable;
259 UINT64 *L3PageTable;
260 UINT64 *L4PageTable;
261 UINT64 *L5PageTable;
262
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;
268
269 if (sizeof (UINTN) == sizeof (UINT64)) {
270 if (Enable5LevelPaging) {
271 L5PageTable = (UINT64 *)PageTableBase;
272 if (L5PageTable[Index5] == 0) {
273 *PageAttribute = PageNone;
274 return NULL;
275 }
276
277 L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
278 } else {
279 L4PageTable = (UINT64 *)PageTableBase;
280 }
281
282 if (L4PageTable[Index4] == 0) {
283 *PageAttribute = PageNone;
284 return NULL;
285 }
286
287 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
288 } else {
289 L3PageTable = (UINT64 *)PageTableBase;
290 }
291
292 if (L3PageTable[Index3] == 0) {
293 *PageAttribute = PageNone;
294 return NULL;
295 }
296
297 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
298 // 1G
299 *PageAttribute = Page1G;
300 return &L3PageTable[Index3];
301 }
302
303 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
304 if (L2PageTable[Index2] == 0) {
305 *PageAttribute = PageNone;
306 return NULL;
307 }
308
309 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
310 // 2M
311 *PageAttribute = Page2M;
312 return &L2PageTable[Index2];
313 }
314
315 // 4k
316 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
317 if ((L1PageTable[Index1] == 0) && (Address != 0)) {
318 *PageAttribute = PageNone;
319 return NULL;
320 }
321
322 *PageAttribute = Page4K;
323 return &L1PageTable[Index1];
324 }
325
326 /**
327 Return memory attributes of page entry.
328
329 @param[in] PageEntry The page entry.
330
331 @return Memory attributes of page entry.
332 **/
333 UINT64
334 GetAttributesFromPageEntry (
335 IN UINT64 *PageEntry
336 )
337 {
338 UINT64 Attributes;
339
340 Attributes = 0;
341 if ((*PageEntry & IA32_PG_P) == 0) {
342 Attributes |= EFI_MEMORY_RP;
343 }
344
345 if ((*PageEntry & IA32_PG_RW) == 0) {
346 Attributes |= EFI_MEMORY_RO;
347 }
348
349 if ((*PageEntry & IA32_PG_NX) != 0) {
350 Attributes |= EFI_MEMORY_XP;
351 }
352
353 return Attributes;
354 }
355
356 /**
357 Modify memory attributes of page entry.
358
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.
363 **/
364 VOID
365 ConvertPageEntryAttribute (
366 IN UINT64 *PageEntry,
367 IN UINT64 Attributes,
368 IN BOOLEAN IsSet,
369 OUT BOOLEAN *IsModified
370 )
371 {
372 UINT64 CurrentPageEntry;
373 UINT64 NewPageEntry;
374
375 CurrentPageEntry = *PageEntry;
376 NewPageEntry = CurrentPageEntry;
377 if ((Attributes & EFI_MEMORY_RP) != 0) {
378 if (IsSet) {
379 NewPageEntry &= ~(UINT64)IA32_PG_P;
380 } else {
381 NewPageEntry |= IA32_PG_P;
382 }
383 }
384
385 if ((Attributes & EFI_MEMORY_RO) != 0) {
386 if (IsSet) {
387 NewPageEntry &= ~(UINT64)IA32_PG_RW;
388 if (mIsShadowStack) {
389 // Environment setup
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;
394 } else {
395 // Runtime update
396 // Clear dirty bit for non shadow stack, to protect RO page.
397 NewPageEntry &= ~(UINT64)IA32_PG_D;
398 }
399 } else {
400 NewPageEntry |= IA32_PG_RW;
401 }
402 }
403
404 if ((Attributes & EFI_MEMORY_XP) != 0) {
405 if (mXdSupported) {
406 if (IsSet) {
407 NewPageEntry |= IA32_PG_NX;
408 } else {
409 NewPageEntry &= ~IA32_PG_NX;
410 }
411 }
412 }
413
414 *PageEntry = NewPageEntry;
415 if (CurrentPageEntry != NewPageEntry) {
416 *IsModified = TRUE;
417 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));
418 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));
419 } else {
420 *IsModified = FALSE;
421 }
422 }
423
424 /**
425 This function returns if there is need to split page entry.
426
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.
431
432 @retval SplitAttributes on if there is need to split page entry.
433 **/
434 PAGE_ATTRIBUTE
435 NeedSplitPage (
436 IN PHYSICAL_ADDRESS BaseAddress,
437 IN UINT64 Length,
438 IN UINT64 *PageEntry,
439 IN PAGE_ATTRIBUTE PageAttribute
440 )
441 {
442 UINT64 PageEntryLength;
443
444 PageEntryLength = PageAttributeToLength (PageAttribute);
445
446 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {
447 return PageNone;
448 }
449
450 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {
451 return Page4K;
452 }
453
454 return Page2M;
455 }
456
457 /**
458 This function splits one page entry to small page entries.
459
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.
463
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.
467 **/
468 RETURN_STATUS
469 SplitPage (
470 IN UINT64 *PageEntry,
471 IN PAGE_ATTRIBUTE PageAttribute,
472 IN PAGE_ATTRIBUTE SplitAttribute
473 )
474 {
475 UINT64 BaseAddress;
476 UINT64 *NewPageEntry;
477 UINTN Index;
478
479 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);
480
481 if (PageAttribute == Page2M) {
482 //
483 // Split 2M to 4K
484 //
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;
491 }
492
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);
496 }
497
498 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
499 return RETURN_SUCCESS;
500 } else {
501 return RETURN_UNSUPPORTED;
502 }
503 } else if (PageAttribute == Page1G) {
504 //
505 // Split 1G to 2M
506 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
507 //
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;
514 }
515
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);
519 }
520
521 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
522 return RETURN_SUCCESS;
523 } else {
524 return RETURN_UNSUPPORTED;
525 }
526 } else {
527 return RETURN_UNSUPPORTED;
528 }
529 }
530
531 /**
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.
534
535 Caller should make sure BaseAddress and Length is at page boundary.
536
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.
545
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.
558 **/
559 RETURN_STATUS
560 ConvertMemoryPageAttributes (
561 IN UINTN PageTableBase,
562 IN BOOLEAN EnablePML5Paging,
563 IN PHYSICAL_ADDRESS BaseAddress,
564 IN UINT64 Length,
565 IN UINT64 Attributes,
566 IN BOOLEAN IsSet,
567 OUT BOOLEAN *IsSplitted OPTIONAL,
568 OUT BOOLEAN *IsModified OPTIONAL
569 )
570 {
571 UINT64 *PageEntry;
572 PAGE_ATTRIBUTE PageAttribute;
573 UINTN PageEntryLength;
574 PAGE_ATTRIBUTE SplitAttribute;
575 RETURN_STATUS Status;
576 BOOLEAN IsEntryModified;
577 EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress;
578
579 ASSERT (Attributes != 0);
580 ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0);
581
582 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
583 ASSERT ((Length & (SIZE_4KB - 1)) == 0);
584
585 if (Length == 0) {
586 return RETURN_INVALID_PARAMETER;
587 }
588
589 MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);
590 if (BaseAddress > MaximumSupportMemAddress) {
591 return RETURN_UNSUPPORTED;
592 }
593
594 if (Length > MaximumSupportMemAddress) {
595 return RETURN_UNSUPPORTED;
596 }
597
598 if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {
599 return RETURN_UNSUPPORTED;
600 }
601
602 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
603
604 if (IsSplitted != NULL) {
605 *IsSplitted = FALSE;
606 }
607
608 if (IsModified != NULL) {
609 *IsModified = FALSE;
610 }
611
612 //
613 // Below logic is to check 2M/4K page to make sure we do not waste memory.
614 //
615 while (Length != 0) {
616 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttribute);
617 if (PageEntry == NULL) {
618 return RETURN_UNSUPPORTED;
619 }
620
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) {
627 *IsModified = TRUE;
628 }
629 }
630
631 //
632 // Convert success, move to next
633 //
634 BaseAddress += PageEntryLength;
635 Length -= PageEntryLength;
636 } else {
637 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);
638 if (RETURN_ERROR (Status)) {
639 return RETURN_UNSUPPORTED;
640 }
641
642 if (IsSplitted != NULL) {
643 *IsSplitted = TRUE;
644 }
645
646 if (IsModified != NULL) {
647 *IsModified = TRUE;
648 }
649
650 //
651 // Just split current page
652 // Convert success in next around
653 //
654 }
655 }
656
657 return RETURN_SUCCESS;
658 }
659
660 /**
661 FlushTlb on current processor.
662
663 @param[in,out] Buffer Pointer to private data buffer.
664 **/
665 VOID
666 EFIAPI
667 FlushTlbOnCurrentProcessor (
668 IN OUT VOID *Buffer
669 )
670 {
671 CpuFlushTlb ();
672 }
673
674 /**
675 FlushTlb for all processors.
676 **/
677 VOID
678 FlushTlbForAll (
679 VOID
680 )
681 {
682 UINTN Index;
683
684 FlushTlbOnCurrentProcessor (NULL);
685
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.
691 }
692 }
693 }
694
695 /**
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.
698
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.
705
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.
718
719 **/
720 EFI_STATUS
721 SmmSetMemoryAttributesEx (
722 IN UINTN PageTableBase,
723 IN BOOLEAN EnablePML5Paging,
724 IN EFI_PHYSICAL_ADDRESS BaseAddress,
725 IN UINT64 Length,
726 IN UINT64 Attributes,
727 OUT BOOLEAN *IsSplitted OPTIONAL
728 )
729 {
730 EFI_STATUS Status;
731 BOOLEAN IsModified;
732
733 Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);
734 if (!EFI_ERROR (Status)) {
735 if (IsModified) {
736 //
737 // Flush TLB as last step
738 //
739 FlushTlbForAll ();
740 }
741 }
742
743 return Status;
744 }
745
746 /**
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.
749
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.
756
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.
769
770 **/
771 EFI_STATUS
772 SmmClearMemoryAttributesEx (
773 IN UINTN PageTableBase,
774 IN BOOLEAN EnablePML5Paging,
775 IN EFI_PHYSICAL_ADDRESS BaseAddress,
776 IN UINT64 Length,
777 IN UINT64 Attributes,
778 OUT BOOLEAN *IsSplitted OPTIONAL
779 )
780 {
781 EFI_STATUS Status;
782 BOOLEAN IsModified;
783
784 Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);
785 if (!EFI_ERROR (Status)) {
786 if (IsModified) {
787 //
788 // Flush TLB as last step
789 //
790 FlushTlbForAll ();
791 }
792 }
793
794 return Status;
795 }
796
797 /**
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.
800
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.
804
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.
817
818 **/
819 EFI_STATUS
820 SmmSetMemoryAttributes (
821 IN EFI_PHYSICAL_ADDRESS BaseAddress,
822 IN UINT64 Length,
823 IN UINT64 Attributes
824 )
825 {
826 IA32_CR4 Cr4;
827 UINTN PageTableBase;
828 BOOLEAN Enable5LevelPaging;
829
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);
834 }
835
836 /**
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.
839
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.
843
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.
856
857 **/
858 EFI_STATUS
859 SmmClearMemoryAttributes (
860 IN EFI_PHYSICAL_ADDRESS BaseAddress,
861 IN UINT64 Length,
862 IN UINT64 Attributes
863 )
864 {
865 IA32_CR4 Cr4;
866 UINTN PageTableBase;
867 BOOLEAN Enable5LevelPaging;
868
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);
873 }
874
875 /**
876 Set ShadowStack memory.
877
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.
881
882 @retval EFI_SUCCESS The shadow stack memory is set.
883 **/
884 EFI_STATUS
885 SetShadowStack (
886 IN UINTN Cr3,
887 IN EFI_PHYSICAL_ADDRESS BaseAddress,
888 IN UINT64 Length
889 )
890 {
891 EFI_STATUS Status;
892
893 mIsShadowStack = TRUE;
894 Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RO, NULL);
895 mIsShadowStack = FALSE;
896
897 return Status;
898 }
899
900 /**
901 Set not present memory.
902
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.
906
907 @retval EFI_SUCCESS The not present memory is set.
908 **/
909 EFI_STATUS
910 SetNotPresentPage (
911 IN UINTN Cr3,
912 IN EFI_PHYSICAL_ADDRESS BaseAddress,
913 IN UINT64 Length
914 )
915 {
916 EFI_STATUS Status;
917
918 Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RP, NULL);
919 return Status;
920 }
921
922 /**
923 Retrieves a pointer to the system configuration table from the SMM System Table
924 based on a specified GUID.
925
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.
928
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.
931
932 **/
933 EFI_STATUS
934 EFIAPI
935 SmmGetSystemConfigurationTable (
936 IN EFI_GUID *TableGuid,
937 OUT VOID **Table
938 )
939 {
940 UINTN Index;
941
942 ASSERT (TableGuid != NULL);
943 ASSERT (Table != NULL);
944
945 *Table = NULL;
946 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {
947 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {
948 *Table = gSmst->SmmConfigurationTable[Index].VendorTable;
949 return EFI_SUCCESS;
950 }
951 }
952
953 return EFI_NOT_FOUND;
954 }
955
956 /**
957 This function sets SMM save state buffer to be RW and XP.
958 **/
959 VOID
960 PatchSmmSaveStateMap (
961 VOID
962 )
963 {
964 UINTN Index;
965 UINTN TileCodeSize;
966 UINTN TileDataSize;
967 UINTN TileSize;
968
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);
975
976 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));
977 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {
978 //
979 // Code
980 //
981 SmmSetMemoryAttributes (
982 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
983 TileCodeSize,
984 EFI_MEMORY_RO
985 );
986 SmmClearMemoryAttributes (
987 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
988 TileCodeSize,
989 EFI_MEMORY_XP
990 );
991
992 //
993 // Data
994 //
995 SmmClearMemoryAttributes (
996 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
997 TileSize - TileCodeSize,
998 EFI_MEMORY_RO
999 );
1000 SmmSetMemoryAttributes (
1001 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
1002 TileSize - TileCodeSize,
1003 EFI_MEMORY_XP
1004 );
1005 }
1006
1007 //
1008 // Code
1009 //
1010 SmmSetMemoryAttributes (
1011 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
1012 TileCodeSize,
1013 EFI_MEMORY_RO
1014 );
1015 SmmClearMemoryAttributes (
1016 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
1017 TileCodeSize,
1018 EFI_MEMORY_XP
1019 );
1020
1021 //
1022 // Data
1023 //
1024 SmmClearMemoryAttributes (
1025 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
1026 SIZE_32KB - TileCodeSize,
1027 EFI_MEMORY_RO
1028 );
1029 SmmSetMemoryAttributes (
1030 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
1031 SIZE_32KB - TileCodeSize,
1032 EFI_MEMORY_XP
1033 );
1034 }
1035
1036 /**
1037 This function sets GDT/IDT buffer to be RO and XP.
1038 **/
1039 VOID
1040 PatchGdtIdtMap (
1041 VOID
1042 )
1043 {
1044 EFI_PHYSICAL_ADDRESS BaseAddress;
1045 UINTN Size;
1046
1047 //
1048 // GDT
1049 //
1050 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));
1051
1052 BaseAddress = mGdtBuffer;
1053 Size = ALIGN_VALUE (mGdtBufferSize, SIZE_4KB);
1054 //
1055 // The range should have been set to RO
1056 // if it is allocated with EfiRuntimeServicesCode.
1057 //
1058 SmmSetMemoryAttributes (
1059 BaseAddress,
1060 Size,
1061 EFI_MEMORY_XP
1062 );
1063
1064 //
1065 // IDT
1066 //
1067 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));
1068
1069 BaseAddress = gcSmiIdtr.Base;
1070 Size = ALIGN_VALUE (gcSmiIdtr.Limit + 1, SIZE_4KB);
1071 //
1072 // The range should have been set to RO
1073 // if it is allocated with EfiRuntimeServicesCode.
1074 //
1075 SmmSetMemoryAttributes (
1076 BaseAddress,
1077 Size,
1078 EFI_MEMORY_XP
1079 );
1080 }
1081
1082 /**
1083 This function sets memory attribute according to MemoryAttributesTable.
1084 **/
1085 VOID
1086 SetMemMapAttributes (
1087 VOID
1088 )
1089 {
1090 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1091 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
1092 UINTN MemoryMapEntryCount;
1093 UINTN DescriptorSize;
1094 UINTN Index;
1095 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
1096
1097 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
1098 if (MemoryAttributesTable == NULL) {
1099 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));
1100 return;
1101 }
1102
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));
1107
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);
1120 }
1121
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),
1130 EFI_MEMORY_RO
1131 );
1132 break;
1133 case EfiRuntimeServicesData:
1134 SmmSetMemoryAttributes (
1135 MemoryMap->PhysicalStart,
1136 EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
1137 EFI_MEMORY_XP
1138 );
1139 break;
1140 default:
1141 SmmSetMemoryAttributes (
1142 MemoryMap->PhysicalStart,
1143 EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
1144 EFI_MEMORY_XP
1145 );
1146 break;
1147 }
1148
1149 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);
1150 }
1151
1152 PatchSmmSaveStateMap ();
1153 PatchGdtIdtMap ();
1154
1155 return;
1156 }
1157
1158 /**
1159 Sort memory map entries based upon PhysicalStart, from low to high.
1160
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.
1165 **/
1166 STATIC
1167 VOID
1168 SortMemoryMap (
1169 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
1170 IN UINTN MemoryMapSize,
1171 IN UINTN DescriptorSize
1172 )
1173 {
1174 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
1175 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
1176 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
1177 EFI_MEMORY_DESCRIPTOR TempMemoryMap;
1178
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));
1188 }
1189
1190 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1191 }
1192
1193 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1194 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1195 }
1196 }
1197
1198 /**
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.
1203 Or return FALSE.
1204
1205 @param[in] MemoryMap A pointer to the memory descriptor.
1206
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.
1209 **/
1210 BOOLEAN
1211 IsUefiPageNotPresent (
1212 IN EFI_MEMORY_DESCRIPTOR *MemoryMap
1213 )
1214 {
1215 switch (MemoryMap->Type) {
1216 case EfiLoaderCode:
1217 case EfiLoaderData:
1218 case EfiBootServicesCode:
1219 case EfiBootServicesData:
1220 case EfiConventionalMemory:
1221 case EfiUnusableMemory:
1222 case EfiACPIReclaimMemory:
1223 return TRUE;
1224 default:
1225 return FALSE;
1226 }
1227 }
1228
1229 /**
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.
1234
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.
1242 **/
1243 STATIC
1244 VOID
1245 MergeMemoryMapForNotPresentEntry (
1246 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
1247 IN OUT UINTN *MemoryMapSize,
1248 IN UINTN DescriptorSize
1249 )
1250 {
1251 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
1252 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
1253 UINT64 MemoryBlockLength;
1254 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
1255 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
1256
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);
1263
1264 do {
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))
1269 {
1270 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
1271 if (NewMemoryMapEntry != MemoryMapEntry) {
1272 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
1273 }
1274
1275 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1276 continue;
1277 } else {
1278 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1279 break;
1280 }
1281 } while (TRUE);
1282
1283 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1284 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
1285 }
1286
1287 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
1288
1289 return;
1290 }
1291
1292 /**
1293 This function caches the GCD memory map information.
1294 **/
1295 VOID
1296 GetGcdMemoryMap (
1297 VOID
1298 )
1299 {
1300 UINTN NumberOfDescriptors;
1301 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemSpaceMap;
1302 EFI_STATUS Status;
1303 UINTN Index;
1304
1305 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemSpaceMap);
1306 if (EFI_ERROR (Status)) {
1307 return;
1308 }
1309
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))
1315 )
1316 {
1317 mGcdMemNumberOfDesc++;
1318 }
1319 }
1320
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);
1326 return;
1327 }
1328
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))
1334 )
1335 {
1336 CopyMem (
1337 &mGcdMemSpace[mGcdMemNumberOfDesc],
1338 &MemSpaceMap[Index],
1339 sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR)
1340 );
1341 mGcdMemNumberOfDesc++;
1342 }
1343 }
1344
1345 gBS->FreePool (MemSpaceMap);
1346 }
1347
1348 /**
1349 Get UEFI MemoryAttributesTable.
1350 **/
1351 VOID
1352 GetUefiMemoryAttributesTable (
1353 VOID
1354 )
1355 {
1356 EFI_STATUS Status;
1357 EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
1358 UINTN MemoryAttributesTableSize;
1359
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);
1365 }
1366 }
1367
1368 /**
1369 This function caches the UEFI memory map information.
1370 **/
1371 VOID
1372 GetUefiMemoryMap (
1373 VOID
1374 )
1375 {
1376 EFI_STATUS Status;
1377 UINTN MapKey;
1378 UINT32 DescriptorVersion;
1379 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1380 UINTN UefiMemoryMapSize;
1381
1382 DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));
1383
1384 UefiMemoryMapSize = 0;
1385 MemoryMap = NULL;
1386 Status = gBS->GetMemoryMap (
1387 &UefiMemoryMapSize,
1388 MemoryMap,
1389 &MapKey,
1390 &mUefiDescriptorSize,
1391 &DescriptorVersion
1392 );
1393 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
1394
1395 do {
1396 Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);
1397 ASSERT (MemoryMap != NULL);
1398 if (MemoryMap == NULL) {
1399 return;
1400 }
1401
1402 Status = gBS->GetMemoryMap (
1403 &UefiMemoryMapSize,
1404 MemoryMap,
1405 &MapKey,
1406 &mUefiDescriptorSize,
1407 &DescriptorVersion
1408 );
1409 if (EFI_ERROR (Status)) {
1410 gBS->FreePool (MemoryMap);
1411 MemoryMap = NULL;
1412 }
1413 } while (Status == EFI_BUFFER_TOO_SMALL);
1414
1415 if (MemoryMap == NULL) {
1416 return;
1417 }
1418
1419 SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);
1420 MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);
1421
1422 mUefiMemoryMapSize = UefiMemoryMapSize;
1423 mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);
1424 ASSERT (mUefiMemoryMap != NULL);
1425
1426 gBS->FreePool (MemoryMap);
1427
1428 //
1429 // Get additional information from GCD memory map.
1430 //
1431 GetGcdMemoryMap ();
1432
1433 //
1434 // Get UEFI memory attributes table.
1435 //
1436 GetUefiMemoryAttributesTable ();
1437 }
1438
1439 /**
1440 This function sets UEFI memory attribute according to UEFI memory map.
1441
1442 The normal memory region is marked as not present, such as
1443 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1444 EfiUnusableMemory, EfiACPIReclaimMemory.
1445 **/
1446 VOID
1447 SetUefiMemMapAttributes (
1448 VOID
1449 )
1450 {
1451 EFI_STATUS Status;
1452 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1453 UINTN MemoryMapEntryCount;
1454 UINTN Index;
1455 EFI_MEMORY_DESCRIPTOR *Entry;
1456
1457 DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));
1458
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),
1467 EFI_MEMORY_RP
1468 );
1469 DEBUG ((
1470 DEBUG_INFO,
1471 "UefiMemory protection: 0x%lx - 0x%lx %r\n",
1472 MemoryMap->PhysicalStart,
1473 MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
1474 Status
1475 ));
1476 }
1477
1478 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);
1479 }
1480 }
1481
1482 //
1483 // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
1484 //
1485
1486 //
1487 // Set untested memory as not present.
1488 //
1489 if (mGcdMemSpace != NULL) {
1490 for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
1491 Status = SmmSetMemoryAttributes (
1492 mGcdMemSpace[Index].BaseAddress,
1493 mGcdMemSpace[Index].Length,
1494 EFI_MEMORY_RP
1495 );
1496 DEBUG ((
1497 DEBUG_INFO,
1498 "GcdMemory protection: 0x%lx - 0x%lx %r\n",
1499 mGcdMemSpace[Index].BaseAddress,
1500 mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,
1501 Status
1502 ));
1503 }
1504 }
1505
1506 //
1507 // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().
1508 //
1509
1510 //
1511 // Set UEFI runtime memory with EFI_MEMORY_RO as not present.
1512 //
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),
1521 EFI_MEMORY_RP
1522 );
1523 DEBUG ((
1524 DEBUG_INFO,
1525 "UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",
1526 Entry->PhysicalStart,
1527 Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
1528 Status
1529 ));
1530 }
1531 }
1532
1533 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
1534 }
1535 }
1536
1537 //
1538 // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
1539 //
1540 }
1541
1542 /**
1543 Return if the Address is forbidden as SMM communication buffer.
1544
1545 @param[in] Address the address to be checked
1546
1547 @return TRUE The address is forbidden as SMM communication buffer.
1548 @return FALSE The address is allowed as SMM communication buffer.
1549 **/
1550 BOOLEAN
1551 IsSmmCommBufferForbiddenAddress (
1552 IN UINT64 Address
1553 )
1554 {
1555 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1556 UINTN MemoryMapEntryCount;
1557 UINTN Index;
1558 EFI_MEMORY_DESCRIPTOR *Entry;
1559
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)))
1567 {
1568 return TRUE;
1569 }
1570 }
1571
1572 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);
1573 }
1574 }
1575
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))
1580 {
1581 return TRUE;
1582 }
1583 }
1584 }
1585
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)))
1593 {
1594 return TRUE;
1595 }
1596
1597 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
1598 }
1599 }
1600 }
1601 }
1602
1603 return FALSE;
1604 }
1605
1606 /**
1607 This function set given attributes of the memory region specified by
1608 BaseAddress and Length.
1609
1610 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1611 @param BaseAddress The physical address that is the start address of
1612 a memory region.
1613 @param Length The size in bytes of the memory region.
1614 @param Attributes The bit mask of attributes to set for the memory
1615 region.
1616
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.
1627
1628 **/
1629 EFI_STATUS
1630 EFIAPI
1631 EdkiiSmmSetMemoryAttributes (
1632 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1633 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1634 IN UINT64 Length,
1635 IN UINT64 Attributes
1636 )
1637 {
1638 return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);
1639 }
1640
1641 /**
1642 This function clears given attributes of the memory region specified by
1643 BaseAddress and Length.
1644
1645 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1646 @param BaseAddress The physical address that is the start address of
1647 a memory region.
1648 @param Length The size in bytes of the memory region.
1649 @param Attributes The bit mask of attributes to clear for the memory
1650 region.
1651
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.
1662
1663 **/
1664 EFI_STATUS
1665 EFIAPI
1666 EdkiiSmmClearMemoryAttributes (
1667 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1668 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1669 IN UINT64 Length,
1670 IN UINT64 Attributes
1671 )
1672 {
1673 return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);
1674 }
1675
1676 /**
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.
1680
1681 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1682 @param BaseAddress The physical address that is the start address of
1683 a memory region.
1684 @param Length The size in bytes of the memory region.
1685 @param Attributes Pointer to attributes returned.
1686
1687 @retval EFI_SUCCESS The attributes got for the memory region.
1688 @retval EFI_INVALID_PARAMETER Length is zero.
1689 Attributes is NULL.
1690 @retval EFI_NO_MAPPING Attributes are not consistent cross the memory
1691 region.
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.
1695
1696 **/
1697 EFI_STATUS
1698 EFIAPI
1699 EdkiiSmmGetMemoryAttributes (
1700 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1701 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1702 IN UINT64 Length,
1703 OUT UINT64 *Attributes
1704 )
1705 {
1706 EFI_PHYSICAL_ADDRESS Address;
1707 UINT64 *PageEntry;
1708 UINT64 MemAttr;
1709 PAGE_ATTRIBUTE PageAttr;
1710 INT64 Size;
1711 UINTN PageTableBase;
1712 BOOLEAN EnablePML5Paging;
1713 IA32_CR4 Cr4;
1714
1715 if ((Length < SIZE_4KB) || (Attributes == NULL)) {
1716 return EFI_INVALID_PARAMETER;
1717 }
1718
1719 Size = (INT64)Length;
1720 MemAttr = (UINT64)-1;
1721
1722 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
1723 Cr4.UintN = AsmReadCr4 ();
1724 EnablePML5Paging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
1725
1726 do {
1727 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttr);
1728 if ((PageEntry == NULL) || (PageAttr == PageNone)) {
1729 return EFI_UNSUPPORTED;
1730 }
1731
1732 //
1733 // If the memory range is cross page table boundary, make sure they
1734 // share the same attribute. Return EFI_NO_MAPPING if not.
1735 //
1736 *Attributes = GetAttributesFromPageEntry (PageEntry);
1737 if ((MemAttr != (UINT64)-1) && (*Attributes != MemAttr)) {
1738 return EFI_NO_MAPPING;
1739 }
1740
1741 switch (PageAttr) {
1742 case Page4K:
1743 Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;
1744 Size -= (SIZE_4KB - (BaseAddress - Address));
1745 BaseAddress += (SIZE_4KB - (BaseAddress - Address));
1746 break;
1747
1748 case Page2M:
1749 Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;
1750 Size -= SIZE_2MB - (BaseAddress - Address);
1751 BaseAddress += SIZE_2MB - (BaseAddress - Address);
1752 break;
1753
1754 case Page1G:
1755 Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;
1756 Size -= SIZE_1GB - (BaseAddress - Address);
1757 BaseAddress += SIZE_1GB - (BaseAddress - Address);
1758 break;
1759
1760 default:
1761 return EFI_UNSUPPORTED;
1762 }
1763
1764 MemAttr = *Attributes;
1765 } while (Size > 0);
1766
1767 return EFI_SUCCESS;
1768 }
1769
1770 /**
1771 Prevent the memory pages used for SMM page table from been overwritten.
1772 **/
1773 VOID
1774 EnablePageTableProtection (
1775 VOID
1776 )
1777 {
1778 PAGE_TABLE_POOL *HeadPool;
1779 PAGE_TABLE_POOL *Pool;
1780 UINT64 PoolSize;
1781 EFI_PHYSICAL_ADDRESS Address;
1782 UINTN PageTableBase;
1783
1784 if (mPageTablePool == NULL) {
1785 return;
1786 }
1787
1788 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
1789
1790 //
1791 // ConvertMemoryPageAttributes might update mPageTablePool. It's safer to
1792 // remember original one in advance.
1793 //
1794 HeadPool = mPageTablePool;
1795 Pool = HeadPool;
1796 do {
1797 Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;
1798 PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);
1799 //
1800 // Set entire pool including header, used-memory and left free-memory as ReadOnly in SMM page table.
1801 //
1802 ConvertMemoryPageAttributes (PageTableBase, m5LevelPagingNeeded, Address, PoolSize, EFI_MEMORY_RO, TRUE, NULL, NULL);
1803 Pool = Pool->NextPool;
1804 } while (Pool != HeadPool);
1805 }
1806
1807 /**
1808 Return whether memory used by SMM page table need to be set as Read Only.
1809
1810 @retval TRUE Need to set SMM page table as Read Only.
1811 @retval FALSE Do not set SMM page table as Read Only.
1812 **/
1813 BOOLEAN
1814 IfReadOnlyPageTableNeeded (
1815 VOID
1816 )
1817 {
1818 //
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
1825 //
1826 if (!IsRestrictedMemoryAccess () ||
1827 ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) ||
1828 FeaturePcdGet (PcdCpuSmmProfileEnable))
1829 {
1830 if (sizeof (UINTN) == sizeof (UINT64)) {
1831 //
1832 // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time.
1833 //
1834 ASSERT (
1835 !(IsRestrictedMemoryAccess () &&
1836 (PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0)
1837 );
1838
1839 //
1840 // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time.
1841 //
1842 ASSERT (!(IsRestrictedMemoryAccess () && FeaturePcdGet (PcdCpuSmmProfileEnable)));
1843 }
1844
1845 return FALSE;
1846 }
1847
1848 return TRUE;
1849 }
1850
1851 /**
1852 This function sets memory attribute for page table.
1853 **/
1854 VOID
1855 SetPageTableAttributes (
1856 VOID
1857 )
1858 {
1859 BOOLEAN CetEnabled;
1860
1861 if (!IfReadOnlyPageTableNeeded ()) {
1862 return;
1863 }
1864
1865 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
1866
1867 //
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*.
1870 //
1871 CetEnabled = ((AsmReadCr4 () & CR4_CET_ENABLE) != 0) ? TRUE : FALSE;
1872 if (CetEnabled) {
1873 //
1874 // CET must be disabled if WP is disabled.
1875 //
1876 DisableCet ();
1877 }
1878
1879 AsmWriteCr0 (AsmReadCr0 () & ~CR0_WP);
1880
1881 // Set memory used by page table as Read Only.
1882 DEBUG ((DEBUG_INFO, "Start...\n"));
1883 EnablePageTableProtection ();
1884
1885 //
1886 // Enable write protection, after page table attribute updated.
1887 //
1888 AsmWriteCr0 (AsmReadCr0 () | CR0_WP);
1889 mIsReadOnlyPageTable = TRUE;
1890
1891 //
1892 // Flush TLB after mark all page table pool as read only.
1893 //
1894 FlushTlbForAll ();
1895
1896 if (CetEnabled) {
1897 //
1898 // re-enable CET.
1899 //
1900 EnableCet ();
1901 }
1902
1903 return;
1904 }