VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c@ 106901

Last change on this file since 106901 was 105670, checked in by vboxsync, 6 months ago

Devices/EFI/FirmwareNew: Merge edk2-stable-202405 and make it build on aarch64, bugref:4643

  • Property svn:eol-style set to native
File size: 62.5 KB
Line 
1/** @file
2
3Copyright (c) 2016 - 2023, Intel Corporation. All rights reserved.<BR>
4SPDX-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
20EFI_MEMORY_DESCRIPTOR *mUefiMemoryMap;
21UINTN mUefiMemoryMapSize;
22UINTN mUefiDescriptorSize;
23
24EFI_GCD_MEMORY_SPACE_DESCRIPTOR *mGcdMemSpace = NULL;
25UINTN mGcdMemNumberOfDesc = 0;
26
27EFI_MEMORY_ATTRIBUTES_TABLE *mUefiMemoryAttributesTable = NULL;
28
29BOOLEAN mIsShadowStack = FALSE;
30BOOLEAN m5LevelPagingNeeded = FALSE;
31PAGING_MODE mPagingMode = PagingModeMax;
32
33//
34// Global variable to keep track current available memory used as page table.
35//
36PAGE_TABLE_POOL *mPageTablePool = NULL;
37
38//
39// If memory used by SMM page table has been mareked as ReadOnly.
40//
41BOOLEAN mIsReadOnlyPageTable = FALSE;
42
43/**
44 Write unprotect read-only pages if Cr0.Bits.WP is 1.
45
46 @param[out] WriteProtect If Cr0.Bits.WP is enabled.
47
48**/
49VOID
50SmmWriteUnprotectReadOnlyPage (
51 OUT BOOLEAN *WriteProtect
52 )
53{
54 IA32_CR0 Cr0;
55
56 Cr0.UintN = AsmReadCr0 ();
57 *WriteProtect = (Cr0.Bits.WP != 0);
58 if (*WriteProtect) {
59 Cr0.Bits.WP = 0;
60 AsmWriteCr0 (Cr0.UintN);
61 }
62}
63
64/**
65 Write protect read-only pages.
66
67 @param[in] WriteProtect If Cr0.Bits.WP should be enabled.
68
69**/
70VOID
71SmmWriteProtectReadOnlyPage (
72 IN BOOLEAN WriteProtect
73 )
74{
75 IA32_CR0 Cr0;
76
77 if (WriteProtect) {
78 Cr0.UintN = AsmReadCr0 ();
79 Cr0.Bits.WP = 1;
80 AsmWriteCr0 (Cr0.UintN);
81 }
82}
83
84/**
85 Initialize a buffer pool for page table use only.
86
87 To reduce the potential split operation on page table, the pages reserved for
88 page table should be allocated in the times of PAGE_TABLE_POOL_UNIT_PAGES and
89 at the boundary of PAGE_TABLE_POOL_ALIGNMENT. So the page pool is always
90 initialized with number of pages greater than or equal to the given PoolPages.
91
92 Once the pages in the pool are used up, this method should be called again to
93 reserve at least another PAGE_TABLE_POOL_UNIT_PAGES. But usually this won't
94 happen in practice.
95
96 @param PoolPages The least page number of the pool to be created.
97
98 @retval TRUE The pool is initialized successfully.
99 @retval FALSE The memory is out of resource.
100**/
101BOOLEAN
102InitializePageTablePool (
103 IN UINTN PoolPages
104 )
105{
106 VOID *Buffer;
107 BOOLEAN WriteProtect;
108 BOOLEAN CetEnabled;
109
110 //
111 // Always reserve at least PAGE_TABLE_POOL_UNIT_PAGES, including one page for
112 // header.
113 //
114 PoolPages += 1; // Add one page for header.
115 PoolPages = ((PoolPages - 1) / PAGE_TABLE_POOL_UNIT_PAGES + 1) *
116 PAGE_TABLE_POOL_UNIT_PAGES;
117 Buffer = AllocateAlignedPages (PoolPages, PAGE_TABLE_POOL_ALIGNMENT);
118 if (Buffer == NULL) {
119 DEBUG ((DEBUG_ERROR, "ERROR: Out of aligned pages\r\n"));
120 return FALSE;
121 }
122
123 //
124 // Link all pools into a list for easier track later.
125 //
126 if (mPageTablePool == NULL) {
127 mPageTablePool = Buffer;
128 mPageTablePool->NextPool = mPageTablePool;
129 } else {
130 ((PAGE_TABLE_POOL *)Buffer)->NextPool = mPageTablePool->NextPool;
131 mPageTablePool->NextPool = Buffer;
132 mPageTablePool = Buffer;
133 }
134
135 //
136 // Reserve one page for pool header.
137 //
138 mPageTablePool->FreePages = PoolPages - 1;
139 mPageTablePool->Offset = EFI_PAGES_TO_SIZE (1);
140
141 //
142 // If page table memory has been marked as RO, mark the new pool pages as read-only.
143 //
144 if (mIsReadOnlyPageTable) {
145 WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
146
147 SmmSetMemoryAttributes ((EFI_PHYSICAL_ADDRESS)(UINTN)Buffer, EFI_PAGES_TO_SIZE (PoolPages), EFI_MEMORY_RO);
148
149 WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
150 }
151
152 return TRUE;
153}
154
155/**
156 This API provides a way to allocate memory for page table.
157
158 This API can be called more once to allocate memory for page tables.
159
160 Allocates the number of 4KB pages of type EfiRuntimeServicesData and returns a pointer to the
161 allocated buffer. The buffer returned is aligned on a 4KB boundary. If Pages is 0, then NULL
162 is returned. If there is not enough memory remaining to satisfy the request, then NULL is
163 returned.
164
165 @param Pages The number of 4 KB pages to allocate.
166
167 @return A pointer to the allocated buffer or NULL if allocation fails.
168
169**/
170VOID *
171AllocatePageTableMemory (
172 IN UINTN Pages
173 )
174{
175 VOID *Buffer;
176
177 if (Pages == 0) {
178 return NULL;
179 }
180
181 //
182 // Renew the pool if necessary.
183 //
184 if ((mPageTablePool == NULL) ||
185 (Pages > mPageTablePool->FreePages))
186 {
187 if (!InitializePageTablePool (Pages)) {
188 return NULL;
189 }
190 }
191
192 Buffer = (UINT8 *)mPageTablePool + mPageTablePool->Offset;
193
194 mPageTablePool->Offset += EFI_PAGES_TO_SIZE (Pages);
195 mPageTablePool->FreePages -= Pages;
196
197 return Buffer;
198}
199
200/**
201 Return page table entry to match the address.
202
203 @param[in] PageTableBase The page table base.
204 @param[in] Enable5LevelPaging If PML5 paging is enabled.
205 @param[in] Address The address to be checked.
206 @param[out] PageAttributes The page attribute of the page entry.
207
208 @return The page entry.
209**/
210VOID *
211GetPageTableEntry (
212 IN UINTN PageTableBase,
213 IN BOOLEAN Enable5LevelPaging,
214 IN PHYSICAL_ADDRESS Address,
215 OUT PAGE_ATTRIBUTE *PageAttribute
216 )
217{
218 UINTN Index1;
219 UINTN Index2;
220 UINTN Index3;
221 UINTN Index4;
222 UINTN Index5;
223 UINT64 *L1PageTable;
224 UINT64 *L2PageTable;
225 UINT64 *L3PageTable;
226 UINT64 *L4PageTable;
227 UINT64 *L5PageTable;
228
229 Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK;
230 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
231 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
232 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
233 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
234
235 if (sizeof (UINTN) == sizeof (UINT64)) {
236 if (Enable5LevelPaging) {
237 L5PageTable = (UINT64 *)PageTableBase;
238 if (L5PageTable[Index5] == 0) {
239 *PageAttribute = PageNone;
240 return NULL;
241 }
242
243 L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
244 } else {
245 L4PageTable = (UINT64 *)PageTableBase;
246 }
247
248 if (L4PageTable[Index4] == 0) {
249 *PageAttribute = PageNone;
250 return NULL;
251 }
252
253 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
254 } else {
255 L3PageTable = (UINT64 *)PageTableBase;
256 }
257
258 if (L3PageTable[Index3] == 0) {
259 *PageAttribute = PageNone;
260 return NULL;
261 }
262
263 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
264 // 1G
265 *PageAttribute = Page1G;
266 return &L3PageTable[Index3];
267 }
268
269 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
270 if (L2PageTable[Index2] == 0) {
271 *PageAttribute = PageNone;
272 return NULL;
273 }
274
275 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
276 // 2M
277 *PageAttribute = Page2M;
278 return &L2PageTable[Index2];
279 }
280
281 // 4k
282 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
283 if ((L1PageTable[Index1] == 0) && (Address != 0)) {
284 *PageAttribute = PageNone;
285 return NULL;
286 }
287
288 *PageAttribute = Page4K;
289 return &L1PageTable[Index1];
290}
291
292/**
293 Return memory attributes of page entry.
294
295 @param[in] PageEntry The page entry.
296
297 @return Memory attributes of page entry.
298**/
299UINT64
300GetAttributesFromPageEntry (
301 IN UINT64 *PageEntry
302 )
303{
304 UINT64 Attributes;
305
306 Attributes = 0;
307 if ((*PageEntry & IA32_PG_P) == 0) {
308 Attributes |= EFI_MEMORY_RP;
309 }
310
311 if ((*PageEntry & IA32_PG_RW) == 0) {
312 Attributes |= EFI_MEMORY_RO;
313 }
314
315 if ((*PageEntry & IA32_PG_NX) != 0) {
316 Attributes |= EFI_MEMORY_XP;
317 }
318
319 return Attributes;
320}
321
322/**
323 This function modifies the page attributes for the memory region specified by BaseAddress and
324 Length from their current attributes to the attributes specified by Attributes.
325
326 Caller should make sure BaseAddress and Length is at page boundary.
327
328 @param[in] PageTableBase The page table base.
329 @param[in] PagingMode The paging mode.
330 @param[in] BaseAddress The physical address that is the start address of a memory region.
331 @param[in] Length The size in bytes of the memory region.
332 @param[in] Attributes The bit mask of attributes to modify for the memory region.
333 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
334 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
335
336 @retval RETURN_SUCCESS The attributes were modified for the memory region.
337 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
338 BaseAddress and Length cannot be modified.
339 @retval RETURN_INVALID_PARAMETER Length is zero.
340 Attributes specified an illegal combination of attributes that
341 cannot be set together.
342 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
343 the memory resource range.
344 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
345 resource range specified by BaseAddress and Length.
346 The bit mask of attributes is not support for the memory resource
347 range specified by BaseAddress and Length.
348**/
349RETURN_STATUS
350ConvertMemoryPageAttributes (
351 IN UINTN PageTableBase,
352 IN PAGING_MODE PagingMode,
353 IN PHYSICAL_ADDRESS BaseAddress,
354 IN UINT64 Length,
355 IN UINT64 Attributes,
356 IN BOOLEAN IsSet,
357 OUT BOOLEAN *IsModified OPTIONAL
358 )
359{
360 RETURN_STATUS Status;
361 IA32_MAP_ATTRIBUTE PagingAttribute;
362 IA32_MAP_ATTRIBUTE PagingAttrMask;
363 UINTN PageTableBufferSize;
364 VOID *PageTableBuffer;
365 EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress;
366 IA32_MAP_ENTRY *Map;
367 UINTN Count;
368 UINTN Index;
369 UINT64 OverlappedRangeBase;
370 UINT64 OverlappedRangeLimit;
371
372 ASSERT (Attributes != 0);
373 ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0);
374
375 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
376 ASSERT ((Length & (SIZE_4KB - 1)) == 0);
377 ASSERT (PageTableBase != 0);
378
379 if (Length == 0) {
380 return RETURN_INVALID_PARAMETER;
381 }
382
383 MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);
384 if (BaseAddress > MaximumSupportMemAddress) {
385 return RETURN_UNSUPPORTED;
386 }
387
388 if (Length > MaximumSupportMemAddress) {
389 return RETURN_UNSUPPORTED;
390 }
391
392 if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {
393 return RETURN_UNSUPPORTED;
394 }
395
396 if (IsModified != NULL) {
397 *IsModified = FALSE;
398 }
399
400 PagingAttribute.Uint64 = 0;
401 PagingAttribute.Uint64 = mAddressEncMask | BaseAddress;
402 PagingAttrMask.Uint64 = 0;
403
404 if ((Attributes & EFI_MEMORY_RO) != 0) {
405 PagingAttrMask.Bits.ReadWrite = 1;
406 if (IsSet) {
407 PagingAttribute.Bits.ReadWrite = 0;
408 PagingAttrMask.Bits.Dirty = 1;
409 if (mIsShadowStack) {
410 // Environment setup
411 // ReadOnly page need set Dirty bit for shadow stack
412 PagingAttribute.Bits.Dirty = 1;
413 // Clear user bit for supervisor shadow stack
414 PagingAttribute.Bits.UserSupervisor = 0;
415 PagingAttrMask.Bits.UserSupervisor = 1;
416 } else {
417 // Runtime update
418 // Clear dirty bit for non shadow stack, to protect RO page.
419 PagingAttribute.Bits.Dirty = 0;
420 }
421 } else {
422 PagingAttribute.Bits.ReadWrite = 1;
423 }
424 }
425
426 if ((Attributes & EFI_MEMORY_XP) != 0) {
427 if (mXdSupported) {
428 PagingAttribute.Bits.Nx = IsSet ? 1 : 0;
429 PagingAttrMask.Bits.Nx = 1;
430 }
431 }
432
433 if ((Attributes & EFI_MEMORY_RP) != 0) {
434 if (IsSet) {
435 PagingAttribute.Bits.Present = 0;
436 //
437 // When map a range to non-present, all attributes except Present should not be provided.
438 //
439 PagingAttrMask.Uint64 = 0;
440 PagingAttrMask.Bits.Present = 1;
441 } else {
442 //
443 // When map range to present range, provide all attributes.
444 //
445 PagingAttribute.Bits.Present = 1;
446 PagingAttrMask.Uint64 = MAX_UINT64;
447
448 //
449 // By default memory is Ring 3 accessble.
450 //
451 PagingAttribute.Bits.UserSupervisor = 1;
452
453 DEBUG_CODE_BEGIN ();
454 if (((Attributes & EFI_MEMORY_RO) == 0) || (((Attributes & EFI_MEMORY_XP) == 0) && (mXdSupported))) {
455 //
456 // When mapping a range to present and EFI_MEMORY_RO or EFI_MEMORY_XP is not specificed,
457 // check if [BaseAddress, BaseAddress + Length] contains present range.
458 // Existing Present range in [BaseAddress, BaseAddress + Length] is set to NX disable or ReadOnly.
459 //
460 Count = 0;
461 Map = NULL;
462 Status = PageTableParse (PageTableBase, mPagingMode, NULL, &Count);
463
464 while (Status == RETURN_BUFFER_TOO_SMALL) {
465 if (Map != NULL) {
466 FreePool (Map);
467 }
468
469 Map = AllocatePool (Count * sizeof (IA32_MAP_ENTRY));
470 ASSERT (Map != NULL);
471 Status = PageTableParse (PageTableBase, mPagingMode, Map, &Count);
472 }
473
474 ASSERT_RETURN_ERROR (Status);
475 for (Index = 0; Index < Count; Index++) {
476 if (Map[Index].LinearAddress >= BaseAddress + Length) {
477 break;
478 }
479
480 if ((BaseAddress < Map[Index].LinearAddress + Map[Index].Length) && (BaseAddress + Length > Map[Index].LinearAddress)) {
481 OverlappedRangeBase = MAX (BaseAddress, Map[Index].LinearAddress);
482 OverlappedRangeLimit = MIN (BaseAddress + Length, Map[Index].LinearAddress + Map[Index].Length);
483
484 if (((Attributes & EFI_MEMORY_RO) == 0) && (Map[Index].Attribute.Bits.ReadWrite == 1)) {
485 DEBUG ((DEBUG_ERROR, "SMM ConvertMemoryPageAttributes: [0x%lx, 0x%lx] is set from ReadWrite to ReadOnly\n", OverlappedRangeBase, OverlappedRangeLimit));
486 }
487
488 if (((Attributes & EFI_MEMORY_XP) == 0) && (mXdSupported) && (Map[Index].Attribute.Bits.Nx == 1)) {
489 DEBUG ((DEBUG_ERROR, "SMM ConvertMemoryPageAttributes: [0x%lx, 0x%lx] is set from NX enabled to NX disabled\n", OverlappedRangeBase, OverlappedRangeLimit));
490 }
491 }
492 }
493
494 FreePool (Map);
495 }
496
497 DEBUG_CODE_END ();
498 }
499 }
500
501 if (PagingAttrMask.Uint64 == 0) {
502 return RETURN_SUCCESS;
503 }
504
505 PageTableBufferSize = 0;
506 Status = PageTableMap (&PageTableBase, PagingMode, NULL, &PageTableBufferSize, BaseAddress, Length, &PagingAttribute, &PagingAttrMask, IsModified);
507
508 if (Status == RETURN_BUFFER_TOO_SMALL) {
509 PageTableBuffer = AllocatePageTableMemory (EFI_SIZE_TO_PAGES (PageTableBufferSize));
510 ASSERT (PageTableBuffer != NULL);
511 Status = PageTableMap (&PageTableBase, PagingMode, PageTableBuffer, &PageTableBufferSize, BaseAddress, Length, &PagingAttribute, &PagingAttrMask, IsModified);
512 }
513
514 if (Status == RETURN_INVALID_PARAMETER) {
515 //
516 // The only reason that PageTableMap returns RETURN_INVALID_PARAMETER here is to modify other attributes
517 // of a non-present range but remains the non-present range still as non-present.
518 //
519 DEBUG ((DEBUG_ERROR, "SMM ConvertMemoryPageAttributes: Only change EFI_MEMORY_XP/EFI_MEMORY_RO for non-present range in [0x%lx, 0x%lx] is not permitted\n", BaseAddress, BaseAddress + Length));
520 }
521
522 ASSERT_RETURN_ERROR (Status);
523 ASSERT (PageTableBufferSize == 0);
524
525 return RETURN_SUCCESS;
526}
527
528/**
529 FlushTlb on current processor.
530
531 @param[in,out] Buffer Pointer to private data buffer.
532**/
533VOID
534EFIAPI
535FlushTlbOnCurrentProcessor (
536 IN OUT VOID *Buffer
537 )
538{
539 CpuFlushTlb ();
540}
541
542/**
543 FlushTlb for all processors.
544**/
545VOID
546FlushTlbForAll (
547 VOID
548 )
549{
550 FlushTlbOnCurrentProcessor (NULL);
551 InternalSmmStartupAllAPs (
552 (EFI_AP_PROCEDURE2)FlushTlbOnCurrentProcessor,
553 0,
554 NULL,
555 NULL,
556 NULL
557 );
558}
559
560/**
561 This function sets the attributes for the memory region specified by BaseAddress and
562 Length from their current attributes to the attributes specified by Attributes.
563
564 @param[in] PageTableBase The page table base.
565 @param[in] PagingMode The paging mode.
566 @param[in] BaseAddress The physical address that is the start address of a memory region.
567 @param[in] Length The size in bytes of the memory region.
568 @param[in] Attributes The bit mask of attributes to set for the memory region.
569
570 @retval EFI_SUCCESS The attributes were set for the memory region.
571 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
572 BaseAddress and Length cannot be modified.
573 @retval EFI_INVALID_PARAMETER Length is zero.
574 Attributes specified an illegal combination of attributes that
575 cannot be set together.
576 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
577 the memory resource range.
578 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
579 resource range specified by BaseAddress and Length.
580 The bit mask of attributes is not support for the memory resource
581 range specified by BaseAddress and Length.
582
583**/
584EFI_STATUS
585SmmSetMemoryAttributesEx (
586 IN UINTN PageTableBase,
587 IN PAGING_MODE PagingMode,
588 IN EFI_PHYSICAL_ADDRESS BaseAddress,
589 IN UINT64 Length,
590 IN UINT64 Attributes
591 )
592{
593 EFI_STATUS Status;
594 BOOLEAN IsModified;
595
596 Status = ConvertMemoryPageAttributes (PageTableBase, PagingMode, BaseAddress, Length, Attributes, TRUE, &IsModified);
597 if (!EFI_ERROR (Status)) {
598 if (IsModified) {
599 //
600 // Flush TLB as last step
601 //
602 FlushTlbForAll ();
603 }
604 }
605
606 return Status;
607}
608
609/**
610 This function clears the attributes for the memory region specified by BaseAddress and
611 Length from their current attributes to the attributes specified by Attributes.
612
613 @param[in] PageTableBase The page table base.
614 @param[in] PagingMode The paging mode.
615 @param[in] BaseAddress The physical address that is the start address of a memory region.
616 @param[in] Length The size in bytes of the memory region.
617 @param[in] Attributes The bit mask of attributes to clear for the memory region.
618
619 @retval EFI_SUCCESS The attributes were cleared for the memory region.
620 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
621 BaseAddress and Length cannot be modified.
622 @retval EFI_INVALID_PARAMETER Length is zero.
623 Attributes specified an illegal combination of attributes that
624 cannot be cleared together.
625 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
626 the memory resource range.
627 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
628 resource range specified by BaseAddress and Length.
629 The bit mask of attributes is not supported for the memory resource
630 range specified by BaseAddress and Length.
631
632**/
633EFI_STATUS
634SmmClearMemoryAttributesEx (
635 IN UINTN PageTableBase,
636 IN PAGING_MODE PagingMode,
637 IN EFI_PHYSICAL_ADDRESS BaseAddress,
638 IN UINT64 Length,
639 IN UINT64 Attributes
640 )
641{
642 EFI_STATUS Status;
643 BOOLEAN IsModified;
644
645 Status = ConvertMemoryPageAttributes (PageTableBase, PagingMode, BaseAddress, Length, Attributes, FALSE, &IsModified);
646 if (!EFI_ERROR (Status)) {
647 if (IsModified) {
648 //
649 // Flush TLB as last step
650 //
651 FlushTlbForAll ();
652 }
653 }
654
655 return Status;
656}
657
658/**
659 This function sets the attributes for the memory region specified by BaseAddress and
660 Length from their current attributes to the attributes specified by Attributes.
661
662 @param[in] BaseAddress The physical address that is the start address of a memory region.
663 @param[in] Length The size in bytes of the memory region.
664 @param[in] Attributes The bit mask of attributes to set for the memory region.
665
666 @retval EFI_SUCCESS The attributes were set for the memory region.
667 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
668 BaseAddress and Length cannot be modified.
669 @retval EFI_INVALID_PARAMETER Length is zero.
670 Attributes specified an illegal combination of attributes that
671 cannot be set together.
672 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
673 the memory resource range.
674 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
675 resource range specified by BaseAddress and Length.
676 The bit mask of attributes is not supported for the memory resource
677 range specified by BaseAddress and Length.
678
679**/
680EFI_STATUS
681SmmSetMemoryAttributes (
682 IN EFI_PHYSICAL_ADDRESS BaseAddress,
683 IN UINT64 Length,
684 IN UINT64 Attributes
685 )
686{
687 UINTN PageTableBase;
688
689 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
690 return SmmSetMemoryAttributesEx (PageTableBase, mPagingMode, BaseAddress, Length, Attributes);
691}
692
693/**
694 This function clears the attributes for the memory region specified by BaseAddress and
695 Length from their current attributes to the attributes specified by Attributes.
696
697 @param[in] BaseAddress The physical address that is the start address of a memory region.
698 @param[in] Length The size in bytes of the memory region.
699 @param[in] Attributes The bit mask of attributes to clear for the memory region.
700
701 @retval EFI_SUCCESS The attributes were cleared for the memory region.
702 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
703 BaseAddress and Length cannot be modified.
704 @retval EFI_INVALID_PARAMETER Length is zero.
705 Attributes specified an illegal combination of attributes that
706 cannot be cleared together.
707 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
708 the memory resource range.
709 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
710 resource range specified by BaseAddress and Length.
711 The bit mask of attributes is not supported for the memory resource
712 range specified by BaseAddress and Length.
713
714**/
715EFI_STATUS
716SmmClearMemoryAttributes (
717 IN EFI_PHYSICAL_ADDRESS BaseAddress,
718 IN UINT64 Length,
719 IN UINT64 Attributes
720 )
721{
722 UINTN PageTableBase;
723
724 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
725 return SmmClearMemoryAttributesEx (PageTableBase, mPagingMode, BaseAddress, Length, Attributes);
726}
727
728/**
729 Set ShadowStack memory.
730
731 @param[in] Cr3 The page table base address.
732 @param[in] BaseAddress The physical address that is the start address of a memory region.
733 @param[in] Length The size in bytes of the memory region.
734
735 @retval EFI_SUCCESS The shadow stack memory is set.
736**/
737EFI_STATUS
738SetShadowStack (
739 IN UINTN Cr3,
740 IN EFI_PHYSICAL_ADDRESS BaseAddress,
741 IN UINT64 Length
742 )
743{
744 EFI_STATUS Status;
745
746 mIsShadowStack = TRUE;
747 Status = SmmSetMemoryAttributesEx (Cr3, mPagingMode, BaseAddress, Length, EFI_MEMORY_RO);
748 mIsShadowStack = FALSE;
749
750 return Status;
751}
752
753/**
754 Retrieves a pointer to the system configuration table from the SMM System Table
755 based on a specified GUID.
756
757 @param[in] TableGuid The pointer to table's GUID type.
758 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.
759
760 @retval EFI_SUCCESS A configuration table matching TableGuid was found.
761 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.
762
763**/
764EFI_STATUS
765EFIAPI
766SmmGetSystemConfigurationTable (
767 IN EFI_GUID *TableGuid,
768 OUT VOID **Table
769 )
770{
771 UINTN Index;
772
773 ASSERT (TableGuid != NULL);
774 ASSERT (Table != NULL);
775
776 *Table = NULL;
777 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {
778 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {
779 *Table = gSmst->SmmConfigurationTable[Index].VendorTable;
780 return EFI_SUCCESS;
781 }
782 }
783
784 return EFI_NOT_FOUND;
785}
786
787/**
788 This function sets SMM save state buffer to be RW and XP.
789**/
790VOID
791PatchSmmSaveStateMap (
792 VOID
793 )
794{
795 UINTN Index;
796 UINTN TileCodeSize;
797 UINTN TileDataSize;
798 UINTN TileSize;
799 UINTN PageTableBase;
800
801 TileCodeSize = GetSmiHandlerSize ();
802 TileCodeSize = ALIGN_VALUE (TileCodeSize, SIZE_4KB);
803 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
804 TileDataSize = ALIGN_VALUE (TileDataSize, SIZE_4KB);
805 TileSize = TileDataSize + TileCodeSize - 1;
806 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
807 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
808
809 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));
810 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {
811 //
812 // Code
813 //
814 ConvertMemoryPageAttributes (
815 PageTableBase,
816 mPagingMode,
817 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
818 TileCodeSize,
819 EFI_MEMORY_RO,
820 TRUE,
821 NULL
822 );
823 ConvertMemoryPageAttributes (
824 PageTableBase,
825 mPagingMode,
826 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
827 TileCodeSize,
828 EFI_MEMORY_XP,
829 FALSE,
830 NULL
831 );
832
833 //
834 // Data
835 //
836 ConvertMemoryPageAttributes (
837 PageTableBase,
838 mPagingMode,
839 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
840 TileSize - TileCodeSize,
841 EFI_MEMORY_RO,
842 FALSE,
843 NULL
844 );
845 ConvertMemoryPageAttributes (
846 PageTableBase,
847 mPagingMode,
848 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
849 TileSize - TileCodeSize,
850 EFI_MEMORY_XP,
851 TRUE,
852 NULL
853 );
854 }
855
856 //
857 // Code
858 //
859 ConvertMemoryPageAttributes (
860 PageTableBase,
861 mPagingMode,
862 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
863 TileCodeSize,
864 EFI_MEMORY_RO,
865 TRUE,
866 NULL
867 );
868 ConvertMemoryPageAttributes (
869 PageTableBase,
870 mPagingMode,
871 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
872 TileCodeSize,
873 EFI_MEMORY_XP,
874 FALSE,
875 NULL
876 );
877
878 //
879 // Data
880 //
881 ConvertMemoryPageAttributes (
882 PageTableBase,
883 mPagingMode,
884 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
885 SIZE_32KB - TileCodeSize,
886 EFI_MEMORY_RO,
887 FALSE,
888 NULL
889 );
890 ConvertMemoryPageAttributes (
891 PageTableBase,
892 mPagingMode,
893 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
894 SIZE_32KB - TileCodeSize,
895 EFI_MEMORY_XP,
896 TRUE,
897 NULL
898 );
899
900 FlushTlbForAll ();
901}
902
903/**
904 This function sets GDT/IDT buffer to be RO and XP.
905**/
906VOID
907PatchGdtIdtMap (
908 VOID
909 )
910{
911 EFI_PHYSICAL_ADDRESS BaseAddress;
912 UINTN Size;
913
914 //
915 // GDT
916 //
917 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));
918
919 BaseAddress = mGdtBuffer;
920 Size = ALIGN_VALUE (mGdtBufferSize, SIZE_4KB);
921 //
922 // The range should have been set to RO
923 // if it is allocated with EfiRuntimeServicesCode.
924 //
925 SmmSetMemoryAttributes (
926 BaseAddress,
927 Size,
928 EFI_MEMORY_XP
929 );
930
931 //
932 // IDT
933 //
934 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));
935
936 BaseAddress = gcSmiIdtr.Base;
937 Size = ALIGN_VALUE (gcSmiIdtr.Limit + 1, SIZE_4KB);
938 //
939 // The range should have been set to RO
940 // if it is allocated with EfiRuntimeServicesCode.
941 //
942 SmmSetMemoryAttributes (
943 BaseAddress,
944 Size,
945 EFI_MEMORY_XP
946 );
947}
948
949/**
950 This function set [Base, Limit] to the input MemoryAttribute.
951
952 @param Base Start address of range.
953 @param Limit Limit address of range.
954 @param Attribute The bit mask of attributes to modify for the memory region.
955 @param Map Pointer to the array of Cr3 IA32_MAP_ENTRY.
956 @param Count Count of IA32_MAP_ENTRY in Map.
957**/
958VOID
959SetMemMapWithNonPresentRange (
960 UINT64 Base,
961 UINT64 Limit,
962 UINT64 Attribute,
963 IA32_MAP_ENTRY *Map,
964 UINTN Count
965 )
966{
967 UINTN Index;
968 UINT64 NonPresentRangeStart;
969
970 NonPresentRangeStart = 0;
971 for (Index = 0; Index < Count; Index++) {
972 if ((Map[Index].LinearAddress > NonPresentRangeStart) &&
973 (Base < Map[Index].LinearAddress) && (Limit > NonPresentRangeStart))
974 {
975 //
976 // We should NOT set attributes for non-present ragne.
977 //
978 //
979 // There is a non-present ( [NonPresentStart, Map[Index].LinearAddress] ) range before current Map[Index]
980 // and it is overlapped with [Base, Limit].
981 //
982 if (Base < NonPresentRangeStart) {
983 SmmSetMemoryAttributes (
984 Base,
985 NonPresentRangeStart - Base,
986 Attribute
987 );
988 }
989
990 Base = Map[Index].LinearAddress;
991 }
992
993 NonPresentRangeStart = Map[Index].LinearAddress + Map[Index].Length;
994 if (NonPresentRangeStart >= Limit) {
995 break;
996 }
997 }
998
999 Limit = MIN (NonPresentRangeStart, Limit);
1000
1001 if (Base < Limit) {
1002 //
1003 // There is no non-present range in current [Base, Limit] anymore.
1004 //
1005 SmmSetMemoryAttributes (
1006 Base,
1007 Limit - Base,
1008 Attribute
1009 );
1010 }
1011}
1012
1013/**
1014 This function sets memory attribute according to MemoryAttributesTable.
1015**/
1016VOID
1017SetMemMapAttributes (
1018 VOID
1019 )
1020{
1021 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1022 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
1023 UINTN MemoryMapEntryCount;
1024 UINTN DescriptorSize;
1025 UINTN Index;
1026 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
1027 UINTN PageTable;
1028 EFI_STATUS Status;
1029 IA32_MAP_ENTRY *Map;
1030 UINTN Count;
1031 UINT64 MemoryAttribute;
1032 BOOLEAN WriteProtect;
1033 BOOLEAN CetEnabled;
1034
1035 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
1036 if (MemoryAttributesTable == NULL) {
1037 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));
1038 return;
1039 }
1040
1041 PERF_FUNCTION_BEGIN ();
1042
1043 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));
1044 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));
1045 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));
1046 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));
1047
1048 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;
1049 DescriptorSize = MemoryAttributesTable->DescriptorSize;
1050 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);
1051 MemoryMap = MemoryMapStart;
1052 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1053 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));
1054 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));
1055 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));
1056 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));
1057 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));
1058 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));
1059 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);
1060 }
1061
1062 Count = 0;
1063 Map = NULL;
1064 PageTable = AsmReadCr3 ();
1065 Status = PageTableParse (PageTable, mPagingMode, NULL, &Count);
1066 while (Status == RETURN_BUFFER_TOO_SMALL) {
1067 if (Map != NULL) {
1068 FreePool (Map);
1069 }
1070
1071 Map = AllocatePool (Count * sizeof (IA32_MAP_ENTRY));
1072 ASSERT (Map != NULL);
1073 Status = PageTableParse (PageTable, mPagingMode, Map, &Count);
1074 }
1075
1076 ASSERT_RETURN_ERROR (Status);
1077
1078 WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
1079
1080 MemoryMap = MemoryMapStart;
1081 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1082 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));
1083 MemoryAttribute = MemoryMap->Attribute & EFI_MEMORY_ACCESS_MASK;
1084 if (MemoryAttribute == 0) {
1085 if (MemoryMap->Type == EfiRuntimeServicesCode) {
1086 MemoryAttribute = EFI_MEMORY_RO;
1087 } else {
1088 ASSERT ((MemoryMap->Type == EfiRuntimeServicesData) || (MemoryMap->Type == EfiConventionalMemory));
1089 //
1090 // Set other type memory as NX.
1091 //
1092 MemoryAttribute = EFI_MEMORY_XP;
1093 }
1094 }
1095
1096 //
1097 // There may exist non-present range overlaps with the MemoryMap range.
1098 // Do not change other attributes of non-present range while still remaining it as non-present
1099 //
1100 SetMemMapWithNonPresentRange (
1101 MemoryMap->PhysicalStart,
1102 MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
1103 MemoryAttribute,
1104 Map,
1105 Count
1106 );
1107
1108 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize);
1109 }
1110
1111 WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
1112
1113 FreePool (Map);
1114
1115 PatchSmmSaveStateMap ();
1116 PatchGdtIdtMap ();
1117
1118 PERF_FUNCTION_END ();
1119}
1120
1121/**
1122 Sort memory map entries based upon PhysicalStart, from low to high.
1123
1124 @param MemoryMap A pointer to the buffer in which firmware places
1125 the current memory map.
1126 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
1127 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
1128**/
1129STATIC
1130VOID
1131SortMemoryMap (
1132 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
1133 IN UINTN MemoryMapSize,
1134 IN UINTN DescriptorSize
1135 )
1136{
1137 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
1138 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
1139 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
1140 EFI_MEMORY_DESCRIPTOR TempMemoryMap;
1141
1142 MemoryMapEntry = MemoryMap;
1143 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1144 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize);
1145 while (MemoryMapEntry < MemoryMapEnd) {
1146 while (NextMemoryMapEntry < MemoryMapEnd) {
1147 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
1148 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
1149 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
1150 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof (EFI_MEMORY_DESCRIPTOR));
1151 }
1152
1153 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1154 }
1155
1156 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1157 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1158 }
1159}
1160
1161/**
1162 Return if a UEFI memory page should be marked as not present in SMM page table.
1163 If the memory map entries type is
1164 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1165 EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.
1166 Or return FALSE.
1167
1168 @param[in] MemoryMap A pointer to the memory descriptor.
1169
1170 @return TRUE The memory described will be marked as not present in SMM page table.
1171 @return FALSE The memory described will not be marked as not present in SMM page table.
1172**/
1173BOOLEAN
1174IsUefiPageNotPresent (
1175 IN EFI_MEMORY_DESCRIPTOR *MemoryMap
1176 )
1177{
1178 switch (MemoryMap->Type) {
1179 case EfiLoaderCode:
1180 case EfiLoaderData:
1181 case EfiBootServicesCode:
1182 case EfiBootServicesData:
1183 case EfiConventionalMemory:
1184 case EfiUnusableMemory:
1185 case EfiACPIReclaimMemory:
1186 return TRUE;
1187 default:
1188 return FALSE;
1189 }
1190}
1191
1192/**
1193 Merge continuous memory map entries whose type is
1194 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1195 EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by
1196 these entries will be set as NOT present in SMM page table.
1197
1198 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
1199 the current memory map.
1200 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
1201 MemoryMap buffer. On input, this is the size of
1202 the current memory map. On output,
1203 it is the size of new memory map after merge.
1204 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
1205**/
1206STATIC
1207VOID
1208MergeMemoryMapForNotPresentEntry (
1209 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
1210 IN OUT UINTN *MemoryMapSize,
1211 IN UINTN DescriptorSize
1212 )
1213{
1214 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
1215 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
1216 UINT64 MemoryBlockLength;
1217 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
1218 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
1219
1220 MemoryMapEntry = MemoryMap;
1221 NewMemoryMapEntry = MemoryMap;
1222 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + *MemoryMapSize);
1223 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
1224 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR));
1225 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1226
1227 do {
1228 MemoryBlockLength = (UINT64)(EFI_PAGES_TO_SIZE ((UINTN)MemoryMapEntry->NumberOfPages));
1229 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
1230 IsUefiPageNotPresent (MemoryMapEntry) && IsUefiPageNotPresent (NextMemoryMapEntry) &&
1231 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart))
1232 {
1233 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
1234 if (NewMemoryMapEntry != MemoryMapEntry) {
1235 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
1236 }
1237
1238 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1239 continue;
1240 } else {
1241 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1242 break;
1243 }
1244 } while (TRUE);
1245
1246 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1247 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
1248 }
1249
1250 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
1251
1252 return;
1253}
1254
1255/**
1256 This function caches the GCD memory map information.
1257**/
1258VOID
1259GetGcdMemoryMap (
1260 VOID
1261 )
1262{
1263 UINTN NumberOfDescriptors;
1264 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemSpaceMap;
1265 EFI_STATUS Status;
1266 UINTN Index;
1267
1268 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemSpaceMap);
1269 if (EFI_ERROR (Status)) {
1270 return;
1271 }
1272
1273 mGcdMemNumberOfDesc = 0;
1274 for (Index = 0; Index < NumberOfDescriptors; Index++) {
1275 if ((MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved) &&
1276 ((MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
1277 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED))
1278 )
1279 {
1280 mGcdMemNumberOfDesc++;
1281 }
1282 }
1283
1284 mGcdMemSpace = AllocateZeroPool (mGcdMemNumberOfDesc * sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR));
1285 ASSERT (mGcdMemSpace != NULL);
1286 if (mGcdMemSpace == NULL) {
1287 mGcdMemNumberOfDesc = 0;
1288 gBS->FreePool (MemSpaceMap);
1289 return;
1290 }
1291
1292 mGcdMemNumberOfDesc = 0;
1293 for (Index = 0; Index < NumberOfDescriptors; Index++) {
1294 if ((MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved) &&
1295 ((MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
1296 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED))
1297 )
1298 {
1299 CopyMem (
1300 &mGcdMemSpace[mGcdMemNumberOfDesc],
1301 &MemSpaceMap[Index],
1302 sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR)
1303 );
1304 mGcdMemNumberOfDesc++;
1305 }
1306 }
1307
1308 gBS->FreePool (MemSpaceMap);
1309}
1310
1311/**
1312 Get UEFI MemoryAttributesTable.
1313**/
1314VOID
1315GetUefiMemoryAttributesTable (
1316 VOID
1317 )
1318{
1319 EFI_STATUS Status;
1320 EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
1321 UINTN MemoryAttributesTableSize;
1322
1323 Status = EfiGetSystemConfigurationTable (&gEfiMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
1324 if (!EFI_ERROR (Status) && (MemoryAttributesTable != NULL)) {
1325 MemoryAttributesTableSize = sizeof (EFI_MEMORY_ATTRIBUTES_TABLE) + MemoryAttributesTable->DescriptorSize * MemoryAttributesTable->NumberOfEntries;
1326 mUefiMemoryAttributesTable = AllocateCopyPool (MemoryAttributesTableSize, MemoryAttributesTable);
1327 ASSERT (mUefiMemoryAttributesTable != NULL);
1328 }
1329}
1330
1331/**
1332 This function caches the UEFI memory map information.
1333**/
1334VOID
1335GetUefiMemoryMap (
1336 VOID
1337 )
1338{
1339 EFI_STATUS Status;
1340 UINTN MapKey;
1341 UINT32 DescriptorVersion;
1342 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1343 UINTN UefiMemoryMapSize;
1344
1345 DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));
1346
1347 UefiMemoryMapSize = 0;
1348 MemoryMap = NULL;
1349 Status = gBS->GetMemoryMap (
1350 &UefiMemoryMapSize,
1351 MemoryMap,
1352 &MapKey,
1353 &mUefiDescriptorSize,
1354 &DescriptorVersion
1355 );
1356 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
1357
1358 do {
1359 Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);
1360 ASSERT (MemoryMap != NULL);
1361 if (MemoryMap == NULL) {
1362 return;
1363 }
1364
1365 Status = gBS->GetMemoryMap (
1366 &UefiMemoryMapSize,
1367 MemoryMap,
1368 &MapKey,
1369 &mUefiDescriptorSize,
1370 &DescriptorVersion
1371 );
1372 if (EFI_ERROR (Status)) {
1373 gBS->FreePool (MemoryMap);
1374 MemoryMap = NULL;
1375 }
1376 } while (Status == EFI_BUFFER_TOO_SMALL);
1377
1378 if (MemoryMap == NULL) {
1379 return;
1380 }
1381
1382 SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);
1383 MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);
1384
1385 mUefiMemoryMapSize = UefiMemoryMapSize;
1386 mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);
1387 ASSERT (mUefiMemoryMap != NULL);
1388
1389 gBS->FreePool (MemoryMap);
1390
1391 //
1392 // Get additional information from GCD memory map.
1393 //
1394 GetGcdMemoryMap ();
1395
1396 //
1397 // Get UEFI memory attributes table.
1398 //
1399 GetUefiMemoryAttributesTable ();
1400}
1401
1402/**
1403 This function sets UEFI memory attribute according to UEFI memory map.
1404
1405 The normal memory region is marked as not present, such as
1406 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1407 EfiUnusableMemory, EfiACPIReclaimMemory.
1408**/
1409VOID
1410SetUefiMemMapAttributes (
1411 VOID
1412 )
1413{
1414 EFI_STATUS Status;
1415 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1416 UINTN MemoryMapEntryCount;
1417 UINTN Index;
1418 EFI_MEMORY_DESCRIPTOR *Entry;
1419 BOOLEAN WriteProtect;
1420 BOOLEAN CetEnabled;
1421
1422 PERF_FUNCTION_BEGIN ();
1423
1424 DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));
1425
1426 WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
1427
1428 if (mUefiMemoryMap != NULL) {
1429 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
1430 MemoryMap = mUefiMemoryMap;
1431 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1432 if (IsUefiPageNotPresent (MemoryMap)) {
1433 Status = SmmSetMemoryAttributes (
1434 MemoryMap->PhysicalStart,
1435 EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
1436 EFI_MEMORY_RP
1437 );
1438 DEBUG ((
1439 DEBUG_INFO,
1440 "UefiMemory protection: 0x%lx - 0x%lx %r\n",
1441 MemoryMap->PhysicalStart,
1442 MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages),
1443 Status
1444 ));
1445 }
1446
1447 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);
1448 }
1449 }
1450
1451 //
1452 // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
1453 //
1454
1455 //
1456 // Set untested memory as not present.
1457 //
1458 if (mGcdMemSpace != NULL) {
1459 for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
1460 Status = SmmSetMemoryAttributes (
1461 mGcdMemSpace[Index].BaseAddress,
1462 mGcdMemSpace[Index].Length,
1463 EFI_MEMORY_RP
1464 );
1465 DEBUG ((
1466 DEBUG_INFO,
1467 "GcdMemory protection: 0x%lx - 0x%lx %r\n",
1468 mGcdMemSpace[Index].BaseAddress,
1469 mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,
1470 Status
1471 ));
1472 }
1473 }
1474
1475 //
1476 // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().
1477 //
1478
1479 //
1480 // Set UEFI runtime memory with EFI_MEMORY_RO as not present.
1481 //
1482 if (mUefiMemoryAttributesTable != NULL) {
1483 Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);
1484 for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {
1485 if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {
1486 if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {
1487 Status = SmmSetMemoryAttributes (
1488 Entry->PhysicalStart,
1489 EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
1490 EFI_MEMORY_RP
1491 );
1492 DEBUG ((
1493 DEBUG_INFO,
1494 "UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",
1495 Entry->PhysicalStart,
1496 Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE ((UINTN)Entry->NumberOfPages),
1497 Status
1498 ));
1499 }
1500 }
1501
1502 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
1503 }
1504 }
1505
1506 WRITE_PROTECT_RO_PAGES (WriteProtect, CetEnabled);
1507
1508 //
1509 // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
1510 //
1511
1512 PERF_FUNCTION_END ();
1513}
1514
1515/**
1516 Return if the Address is forbidden as SMM communication buffer.
1517
1518 @param[in] Address the address to be checked
1519
1520 @return TRUE The address is forbidden as SMM communication buffer.
1521 @return FALSE The address is allowed as SMM communication buffer.
1522**/
1523BOOLEAN
1524IsSmmCommBufferForbiddenAddress (
1525 IN UINT64 Address
1526 )
1527{
1528 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1529 UINTN MemoryMapEntryCount;
1530 UINTN Index;
1531 EFI_MEMORY_DESCRIPTOR *Entry;
1532
1533 if (mUefiMemoryMap != NULL) {
1534 MemoryMap = mUefiMemoryMap;
1535 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
1536 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1537 if (IsUefiPageNotPresent (MemoryMap)) {
1538 if ((Address >= MemoryMap->PhysicalStart) &&
1539 (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages)))
1540 {
1541 return TRUE;
1542 }
1543 }
1544
1545 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, mUefiDescriptorSize);
1546 }
1547 }
1548
1549 if (mGcdMemSpace != NULL) {
1550 for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
1551 if ((Address >= mGcdMemSpace[Index].BaseAddress) &&
1552 (Address < mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length))
1553 {
1554 return TRUE;
1555 }
1556 }
1557 }
1558
1559 if (mUefiMemoryAttributesTable != NULL) {
1560 Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);
1561 for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {
1562 if ((Entry->Type == EfiRuntimeServicesCode) || (Entry->Type == EfiRuntimeServicesData)) {
1563 if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {
1564 if ((Address >= Entry->PhysicalStart) &&
1565 (Address < Entry->PhysicalStart + LShiftU64 (Entry->NumberOfPages, EFI_PAGE_SHIFT)))
1566 {
1567 return TRUE;
1568 }
1569
1570 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
1571 }
1572 }
1573 }
1574 }
1575
1576 return FALSE;
1577}
1578
1579/**
1580 This function set given attributes of the memory region specified by
1581 BaseAddress and Length.
1582
1583 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1584 @param BaseAddress The physical address that is the start address of
1585 a memory region.
1586 @param Length The size in bytes of the memory region.
1587 @param Attributes The bit mask of attributes to set for the memory
1588 region.
1589
1590 @retval EFI_SUCCESS The attributes were set for the memory region.
1591 @retval EFI_INVALID_PARAMETER Length is zero.
1592 Attributes specified an illegal combination of
1593 attributes that cannot be set together.
1594 @retval EFI_UNSUPPORTED The processor does not support one or more
1595 bytes of the memory resource range specified
1596 by BaseAddress and Length.
1597 The bit mask of attributes is not supported for
1598 the memory resource range specified by
1599 BaseAddress and Length.
1600
1601**/
1602EFI_STATUS
1603EFIAPI
1604EdkiiSmmSetMemoryAttributes (
1605 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1606 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1607 IN UINT64 Length,
1608 IN UINT64 Attributes
1609 )
1610{
1611 return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);
1612}
1613
1614/**
1615 This function clears given attributes of the memory region specified by
1616 BaseAddress and Length.
1617
1618 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1619 @param BaseAddress The physical address that is the start address of
1620 a memory region.
1621 @param Length The size in bytes of the memory region.
1622 @param Attributes The bit mask of attributes to clear for the memory
1623 region.
1624
1625 @retval EFI_SUCCESS The attributes were cleared for the memory region.
1626 @retval EFI_INVALID_PARAMETER Length is zero.
1627 Attributes specified an illegal combination of
1628 attributes that cannot be cleared together.
1629 @retval EFI_UNSUPPORTED The processor does not support one or more
1630 bytes of the memory resource range specified
1631 by BaseAddress and Length.
1632 The bit mask of attributes is not supported for
1633 the memory resource range specified by
1634 BaseAddress and Length.
1635
1636**/
1637EFI_STATUS
1638EFIAPI
1639EdkiiSmmClearMemoryAttributes (
1640 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1641 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1642 IN UINT64 Length,
1643 IN UINT64 Attributes
1644 )
1645{
1646 return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);
1647}
1648
1649/**
1650 Create page table based on input PagingMode, LinearAddress and Length.
1651
1652 @param[in, out] PageTable The pointer to the page table.
1653 @param[in] PagingMode The paging mode.
1654 @param[in] LinearAddress The start of the linear address range.
1655 @param[in] Length The length of the linear address range.
1656
1657**/
1658VOID
1659GenPageTable (
1660 IN OUT UINTN *PageTable,
1661 IN PAGING_MODE PagingMode,
1662 IN UINT64 LinearAddress,
1663 IN UINT64 Length
1664 )
1665{
1666 RETURN_STATUS Status;
1667 UINTN PageTableBufferSize;
1668 VOID *PageTableBuffer;
1669 IA32_MAP_ATTRIBUTE MapAttribute;
1670 IA32_MAP_ATTRIBUTE MapMask;
1671
1672 MapMask.Uint64 = MAX_UINT64;
1673 MapAttribute.Uint64 = mAddressEncMask|LinearAddress;
1674 MapAttribute.Bits.Present = 1;
1675 MapAttribute.Bits.ReadWrite = 1;
1676 MapAttribute.Bits.UserSupervisor = 1;
1677 MapAttribute.Bits.Accessed = 1;
1678 MapAttribute.Bits.Dirty = 1;
1679 PageTableBufferSize = 0;
1680
1681 Status = PageTableMap (
1682 PageTable,
1683 PagingMode,
1684 NULL,
1685 &PageTableBufferSize,
1686 LinearAddress,
1687 Length,
1688 &MapAttribute,
1689 &MapMask,
1690 NULL
1691 );
1692 if (Status == RETURN_BUFFER_TOO_SMALL) {
1693 DEBUG ((DEBUG_INFO, "GenSMMPageTable: 0x%x bytes needed for initial SMM page table\n", PageTableBufferSize));
1694 PageTableBuffer = AllocatePageTableMemory (EFI_SIZE_TO_PAGES (PageTableBufferSize));
1695 ASSERT (PageTableBuffer != NULL);
1696 Status = PageTableMap (
1697 PageTable,
1698 PagingMode,
1699 PageTableBuffer,
1700 &PageTableBufferSize,
1701 LinearAddress,
1702 Length,
1703 &MapAttribute,
1704 &MapMask,
1705 NULL
1706 );
1707 }
1708
1709 ASSERT (Status == RETURN_SUCCESS);
1710 ASSERT (PageTableBufferSize == 0);
1711}
1712
1713/**
1714 Create page table based on input PagingMode and PhysicalAddressBits in smm.
1715
1716 @param[in] PagingMode The paging mode.
1717 @param[in] PhysicalAddressBits The bits of physical address to map.
1718
1719 @retval PageTable Address
1720
1721**/
1722UINTN
1723GenSmmPageTable (
1724 IN PAGING_MODE PagingMode,
1725 IN UINT8 PhysicalAddressBits
1726 )
1727{
1728 UINTN PageTable;
1729 RETURN_STATUS Status;
1730 UINTN GuardPage;
1731 UINTN Index;
1732 UINT64 Length;
1733 PAGING_MODE SmramPagingMode;
1734
1735 PageTable = 0;
1736 Length = LShiftU64 (1, PhysicalAddressBits);
1737 ASSERT (Length > mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize);
1738
1739 if (sizeof (UINTN) == sizeof (UINT64)) {
1740 SmramPagingMode = m5LevelPagingNeeded ? Paging5Level4KB : Paging4Level4KB;
1741 } else {
1742 SmramPagingMode = PagingPae4KB;
1743 }
1744
1745 ASSERT (mCpuHotPlugData.SmrrBase % SIZE_4KB == 0);
1746 ASSERT (mCpuHotPlugData.SmrrSize % SIZE_4KB == 0);
1747 GenPageTable (&PageTable, PagingMode, 0, mCpuHotPlugData.SmrrBase);
1748
1749 //
1750 // Map smram range in 4K page granularity to avoid subsequent page split when smm ready to lock.
1751 // If BSP are splitting the 1G/2M paging entries to 512 2M/4K paging entries, and all APs are
1752 // still running in SMI at the same time, which might access the affected linear-address range
1753 // between the time of modification and the time of invalidation access. That will be a potential
1754 // problem leading exception happen.
1755 //
1756 GenPageTable (&PageTable, SmramPagingMode, mCpuHotPlugData.SmrrBase, mCpuHotPlugData.SmrrSize);
1757
1758 GenPageTable (&PageTable, PagingMode, mCpuHotPlugData.SmrrBase + mCpuHotPlugData.SmrrSize, Length - mCpuHotPlugData.SmrrBase - mCpuHotPlugData.SmrrSize);
1759
1760 if (FeaturePcdGet (PcdCpuSmmStackGuard)) {
1761 //
1762 // Mark the 4KB guard page between known good stack and smm stack as non-present
1763 //
1764 for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) {
1765 GuardPage = mSmmStackArrayBase + EFI_PAGE_SIZE + Index * (mSmmStackSize + mSmmShadowStackSize);
1766 Status = ConvertMemoryPageAttributes (PageTable, PagingMode, GuardPage, SIZE_4KB, EFI_MEMORY_RP, TRUE, NULL);
1767 ASSERT (Status == RETURN_SUCCESS);
1768 }
1769 }
1770
1771 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT1) != 0) {
1772 //
1773 // Mark [0, 4k] as non-present
1774 //
1775 Status = ConvertMemoryPageAttributes (PageTable, PagingMode, 0, SIZE_4KB, EFI_MEMORY_RP, TRUE, NULL);
1776 ASSERT (Status == RETURN_SUCCESS);
1777 }
1778
1779 return (UINTN)PageTable;
1780}
1781
1782/**
1783 This function retrieves the attributes of the memory region specified by
1784 BaseAddress and Length. If different attributes are got from different part
1785 of the memory region, EFI_NO_MAPPING will be returned.
1786
1787 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1788 @param BaseAddress The physical address that is the start address of
1789 a memory region.
1790 @param Length The size in bytes of the memory region.
1791 @param Attributes Pointer to attributes returned.
1792
1793 @retval EFI_SUCCESS The attributes got for the memory region.
1794 @retval EFI_INVALID_PARAMETER Length is zero.
1795 Attributes is NULL.
1796 @retval EFI_NO_MAPPING Attributes are not consistent cross the memory
1797 region.
1798 @retval EFI_UNSUPPORTED The processor does not support one or more
1799 bytes of the memory resource range specified
1800 by BaseAddress and Length.
1801
1802**/
1803EFI_STATUS
1804EFIAPI
1805EdkiiSmmGetMemoryAttributes (
1806 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1807 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1808 IN UINT64 Length,
1809 OUT UINT64 *Attributes
1810 )
1811{
1812 EFI_PHYSICAL_ADDRESS Address;
1813 UINT64 *PageEntry;
1814 UINT64 MemAttr;
1815 PAGE_ATTRIBUTE PageAttr;
1816 INT64 Size;
1817 UINTN PageTableBase;
1818 BOOLEAN EnablePML5Paging;
1819 IA32_CR4 Cr4;
1820
1821 if ((Length < SIZE_4KB) || (Attributes == NULL)) {
1822 return EFI_INVALID_PARAMETER;
1823 }
1824
1825 Size = (INT64)Length;
1826 MemAttr = (UINT64)-1;
1827
1828 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
1829 Cr4.UintN = AsmReadCr4 ();
1830 EnablePML5Paging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
1831
1832 do {
1833 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttr);
1834 if ((PageEntry == NULL) || (PageAttr == PageNone)) {
1835 return EFI_UNSUPPORTED;
1836 }
1837
1838 //
1839 // If the memory range is cross page table boundary, make sure they
1840 // share the same attribute. Return EFI_NO_MAPPING if not.
1841 //
1842 *Attributes = GetAttributesFromPageEntry (PageEntry);
1843 if ((MemAttr != (UINT64)-1) && (*Attributes != MemAttr)) {
1844 return EFI_NO_MAPPING;
1845 }
1846
1847 switch (PageAttr) {
1848 case Page4K:
1849 Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;
1850 Size -= (SIZE_4KB - (BaseAddress - Address));
1851 BaseAddress += (SIZE_4KB - (BaseAddress - Address));
1852 break;
1853
1854 case Page2M:
1855 Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;
1856 Size -= SIZE_2MB - (BaseAddress - Address);
1857 BaseAddress += SIZE_2MB - (BaseAddress - Address);
1858 break;
1859
1860 case Page1G:
1861 Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;
1862 Size -= SIZE_1GB - (BaseAddress - Address);
1863 BaseAddress += SIZE_1GB - (BaseAddress - Address);
1864 break;
1865
1866 default:
1867 return EFI_UNSUPPORTED;
1868 }
1869
1870 MemAttr = *Attributes;
1871 } while (Size > 0);
1872
1873 return EFI_SUCCESS;
1874}
1875
1876/**
1877 Prevent the memory pages used for SMM page table from been overwritten.
1878**/
1879VOID
1880EnablePageTableProtection (
1881 VOID
1882 )
1883{
1884 PAGE_TABLE_POOL *HeadPool;
1885 PAGE_TABLE_POOL *Pool;
1886 UINT64 PoolSize;
1887 EFI_PHYSICAL_ADDRESS Address;
1888 UINTN PageTableBase;
1889
1890 if (mPageTablePool == NULL) {
1891 return;
1892 }
1893
1894 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
1895
1896 //
1897 // ConvertMemoryPageAttributes might update mPageTablePool. It's safer to
1898 // remember original one in advance.
1899 //
1900 HeadPool = mPageTablePool;
1901 Pool = HeadPool;
1902 do {
1903 Address = (EFI_PHYSICAL_ADDRESS)(UINTN)Pool;
1904 PoolSize = Pool->Offset + EFI_PAGES_TO_SIZE (Pool->FreePages);
1905 //
1906 // Set entire pool including header, used-memory and left free-memory as ReadOnly in SMM page table.
1907 //
1908 ConvertMemoryPageAttributes (PageTableBase, mPagingMode, Address, PoolSize, EFI_MEMORY_RO, TRUE, NULL);
1909 Pool = Pool->NextPool;
1910 } while (Pool != HeadPool);
1911}
1912
1913/**
1914 Return whether memory used by SMM page table need to be set as Read Only.
1915
1916 @retval TRUE Need to set SMM page table as Read Only.
1917 @retval FALSE Do not set SMM page table as Read Only.
1918**/
1919BOOLEAN
1920IfReadOnlyPageTableNeeded (
1921 VOID
1922 )
1923{
1924 //
1925 // Don't mark page table memory as read-only if
1926 // - no restriction on access to non-SMRAM memory; or
1927 // - SMM heap guard feature enabled; or
1928 // BIT2: SMM page guard enabled
1929 // BIT3: SMM pool guard enabled
1930 // - SMM profile feature enabled
1931 //
1932 if (!IsRestrictedMemoryAccess () ||
1933 ((PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0) ||
1934 FeaturePcdGet (PcdCpuSmmProfileEnable))
1935 {
1936 if (sizeof (UINTN) == sizeof (UINT64)) {
1937 //
1938 // Restriction on access to non-SMRAM memory and heap guard could not be enabled at the same time.
1939 //
1940 ASSERT (
1941 !(IsRestrictedMemoryAccess () &&
1942 (PcdGet8 (PcdHeapGuardPropertyMask) & (BIT3 | BIT2)) != 0)
1943 );
1944
1945 //
1946 // Restriction on access to non-SMRAM memory and SMM profile could not be enabled at the same time.
1947 //
1948 ASSERT (!(IsRestrictedMemoryAccess () && FeaturePcdGet (PcdCpuSmmProfileEnable)));
1949 }
1950
1951 return FALSE;
1952 }
1953
1954 return TRUE;
1955}
1956
1957/**
1958 This function sets memory attribute for page table.
1959**/
1960VOID
1961SetPageTableAttributes (
1962 VOID
1963 )
1964{
1965 BOOLEAN WriteProtect;
1966 BOOLEAN CetEnabled;
1967
1968 if (!IfReadOnlyPageTableNeeded ()) {
1969 return;
1970 }
1971
1972 PERF_FUNCTION_BEGIN ();
1973 DEBUG ((DEBUG_INFO, "SetPageTableAttributes\n"));
1974
1975 //
1976 // Disable write protection, because we need mark page table to be write protected.
1977 // We need *write* page table memory, to mark itself to be *read only*.
1978 //
1979 WRITE_UNPROTECT_RO_PAGES (WriteProtect, CetEnabled);
1980
1981 // Set memory used by page table as Read Only.
1982 DEBUG ((DEBUG_INFO, "Start...\n"));
1983 EnablePageTableProtection ();
1984
1985 //
1986 // Enable write protection, after page table attribute updated.
1987 //
1988 WRITE_PROTECT_RO_PAGES (TRUE, CetEnabled);
1989
1990 mIsReadOnlyPageTable = TRUE;
1991
1992 //
1993 // Flush TLB after mark all page table pool as read only.
1994 //
1995 FlushTlbForAll ();
1996 PERF_FUNCTION_END ();
1997}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette