]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c
UefiCpuPkg: Add New Memory Attributes
[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 UINTN mInternalGr3;
36
37 /**
38 Set the internal page table base address.
39 If it is non zero, further MemoryAttribute modification will be on this page table.
40 If it is zero, further MemoryAttribute modification will be on real page table.
41
42 @param Cr3 page table base.
43 **/
44 VOID
45 SetPageTableBase (
46 IN UINTN Cr3
47 )
48 {
49 mInternalGr3 = Cr3;
50 }
51
52 /**
53 Return page table base.
54
55 @return page table base.
56 **/
57 UINTN
58 GetPageTableBase (
59 VOID
60 )
61 {
62 if (mInternalGr3 != 0) {
63 return mInternalGr3;
64 }
65 return (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64);
66 }
67
68 /**
69 Return length according to page attributes.
70
71 @param[in] PageAttributes The page attribute of the page entry.
72
73 @return The length of page entry.
74 **/
75 UINTN
76 PageAttributeToLength (
77 IN PAGE_ATTRIBUTE PageAttribute
78 )
79 {
80 UINTN Index;
81 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
82 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
83 return (UINTN)mPageAttributeTable[Index].Length;
84 }
85 }
86 return 0;
87 }
88
89 /**
90 Return address mask according to page attributes.
91
92 @param[in] PageAttributes The page attribute of the page entry.
93
94 @return The address mask of page entry.
95 **/
96 UINTN
97 PageAttributeToMask (
98 IN PAGE_ATTRIBUTE PageAttribute
99 )
100 {
101 UINTN Index;
102 for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) {
103 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
104 return (UINTN)mPageAttributeTable[Index].AddressMask;
105 }
106 }
107 return 0;
108 }
109
110 /**
111 Return page table entry to match the address.
112
113 @param[in] Address The address to be checked.
114 @param[out] PageAttributes The page attribute of the page entry.
115
116 @return The page entry.
117 **/
118 VOID *
119 GetPageTableEntry (
120 IN PHYSICAL_ADDRESS Address,
121 OUT PAGE_ATTRIBUTE *PageAttribute
122 )
123 {
124 UINTN Index1;
125 UINTN Index2;
126 UINTN Index3;
127 UINTN Index4;
128 UINTN Index5;
129 UINT64 *L1PageTable;
130 UINT64 *L2PageTable;
131 UINT64 *L3PageTable;
132 UINT64 *L4PageTable;
133 UINT64 *L5PageTable;
134 IA32_CR4 Cr4;
135 BOOLEAN Enable5LevelPaging;
136
137 Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK;
138 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
139 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
140 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
141 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
142
143 Cr4.UintN = AsmReadCr4 ();
144 Enable5LevelPaging = (BOOLEAN) (Cr4.Bits.LA57 == 1);
145
146 if (sizeof(UINTN) == sizeof(UINT64)) {
147 if (Enable5LevelPaging) {
148 L5PageTable = (UINT64 *)GetPageTableBase ();
149 if (L5PageTable[Index5] == 0) {
150 *PageAttribute = PageNone;
151 return NULL;
152 }
153
154 L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
155 } else {
156 L4PageTable = (UINT64 *)GetPageTableBase ();
157 }
158 if (L4PageTable[Index4] == 0) {
159 *PageAttribute = PageNone;
160 return NULL;
161 }
162
163 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
164 } else {
165 L3PageTable = (UINT64 *)GetPageTableBase ();
166 }
167 if (L3PageTable[Index3] == 0) {
168 *PageAttribute = PageNone;
169 return NULL;
170 }
171 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
172 // 1G
173 *PageAttribute = Page1G;
174 return &L3PageTable[Index3];
175 }
176
177 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
178 if (L2PageTable[Index2] == 0) {
179 *PageAttribute = PageNone;
180 return NULL;
181 }
182 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
183 // 2M
184 *PageAttribute = Page2M;
185 return &L2PageTable[Index2];
186 }
187
188 // 4k
189 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
190 if ((L1PageTable[Index1] == 0) && (Address != 0)) {
191 *PageAttribute = PageNone;
192 return NULL;
193 }
194 *PageAttribute = Page4K;
195 return &L1PageTable[Index1];
196 }
197
198 /**
199 Return memory attributes of page entry.
200
201 @param[in] PageEntry The page entry.
202
203 @return Memory attributes of page entry.
204 **/
205 UINT64
206 GetAttributesFromPageEntry (
207 IN UINT64 *PageEntry
208 )
209 {
210 UINT64 Attributes;
211 Attributes = 0;
212 if ((*PageEntry & IA32_PG_P) == 0) {
213 Attributes |= EFI_MEMORY_RP;
214 }
215 if ((*PageEntry & IA32_PG_RW) == 0) {
216 Attributes |= EFI_MEMORY_RO;
217 }
218 if ((*PageEntry & IA32_PG_NX) != 0) {
219 Attributes |= EFI_MEMORY_XP;
220 }
221 return Attributes;
222 }
223
224 /**
225 Modify memory attributes of page entry.
226
227 @param[in] PageEntry The page entry.
228 @param[in] Attributes The bit mask of attributes to modify for the memory region.
229 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
230 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
231 **/
232 VOID
233 ConvertPageEntryAttribute (
234 IN UINT64 *PageEntry,
235 IN UINT64 Attributes,
236 IN BOOLEAN IsSet,
237 OUT BOOLEAN *IsModified
238 )
239 {
240 UINT64 CurrentPageEntry;
241 UINT64 NewPageEntry;
242
243 CurrentPageEntry = *PageEntry;
244 NewPageEntry = CurrentPageEntry;
245 if ((Attributes & EFI_MEMORY_RP) != 0) {
246 if (IsSet) {
247 NewPageEntry &= ~(UINT64)IA32_PG_P;
248 } else {
249 NewPageEntry |= IA32_PG_P;
250 }
251 }
252 if ((Attributes & EFI_MEMORY_RO) != 0) {
253 if (IsSet) {
254 NewPageEntry &= ~(UINT64)IA32_PG_RW;
255 if (mInternalGr3 != 0) {
256 // Environment setup
257 // ReadOnly page need set Dirty bit for shadow stack
258 NewPageEntry |= IA32_PG_D;
259 // Clear user bit for supervisor shadow stack
260 NewPageEntry &= ~(UINT64)IA32_PG_U;
261 } else {
262 // Runtime update
263 // Clear dirty bit for non shadow stack, to protect RO page.
264 NewPageEntry &= ~(UINT64)IA32_PG_D;
265 }
266 } else {
267 NewPageEntry |= IA32_PG_RW;
268 }
269 }
270 if ((Attributes & EFI_MEMORY_XP) != 0) {
271 if (mXdSupported) {
272 if (IsSet) {
273 NewPageEntry |= IA32_PG_NX;
274 } else {
275 NewPageEntry &= ~IA32_PG_NX;
276 }
277 }
278 }
279 *PageEntry = NewPageEntry;
280 if (CurrentPageEntry != NewPageEntry) {
281 *IsModified = TRUE;
282 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));
283 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));
284 } else {
285 *IsModified = FALSE;
286 }
287 }
288
289 /**
290 This function returns if there is need to split page entry.
291
292 @param[in] BaseAddress The base address to be checked.
293 @param[in] Length The length to be checked.
294 @param[in] PageEntry The page entry to be checked.
295 @param[in] PageAttribute The page attribute of the page entry.
296
297 @retval SplitAttributes on if there is need to split page entry.
298 **/
299 PAGE_ATTRIBUTE
300 NeedSplitPage (
301 IN PHYSICAL_ADDRESS BaseAddress,
302 IN UINT64 Length,
303 IN UINT64 *PageEntry,
304 IN PAGE_ATTRIBUTE PageAttribute
305 )
306 {
307 UINT64 PageEntryLength;
308
309 PageEntryLength = PageAttributeToLength (PageAttribute);
310
311 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {
312 return PageNone;
313 }
314
315 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {
316 return Page4K;
317 }
318
319 return Page2M;
320 }
321
322 /**
323 This function splits one page entry to small page entries.
324
325 @param[in] PageEntry The page entry to be splitted.
326 @param[in] PageAttribute The page attribute of the page entry.
327 @param[in] SplitAttribute How to split the page entry.
328
329 @retval RETURN_SUCCESS The page entry is splitted.
330 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
331 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
332 **/
333 RETURN_STATUS
334 SplitPage (
335 IN UINT64 *PageEntry,
336 IN PAGE_ATTRIBUTE PageAttribute,
337 IN PAGE_ATTRIBUTE SplitAttribute
338 )
339 {
340 UINT64 BaseAddress;
341 UINT64 *NewPageEntry;
342 UINTN Index;
343
344 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);
345
346 if (PageAttribute == Page2M) {
347 //
348 // Split 2M to 4K
349 //
350 ASSERT (SplitAttribute == Page4K);
351 if (SplitAttribute == Page4K) {
352 NewPageEntry = AllocatePageTableMemory (1);
353 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
354 if (NewPageEntry == NULL) {
355 return RETURN_OUT_OF_RESOURCES;
356 }
357 BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;
358 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
359 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);
360 }
361 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
362 return RETURN_SUCCESS;
363 } else {
364 return RETURN_UNSUPPORTED;
365 }
366 } else if (PageAttribute == Page1G) {
367 //
368 // Split 1G to 2M
369 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
370 //
371 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);
372 if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) {
373 NewPageEntry = AllocatePageTableMemory (1);
374 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
375 if (NewPageEntry == NULL) {
376 return RETURN_OUT_OF_RESOURCES;
377 }
378 BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64;
379 for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) {
380 NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS);
381 }
382 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
383 return RETURN_SUCCESS;
384 } else {
385 return RETURN_UNSUPPORTED;
386 }
387 } else {
388 return RETURN_UNSUPPORTED;
389 }
390 }
391
392 /**
393 This function modifies the page attributes for the memory region specified by BaseAddress and
394 Length from their current attributes to the attributes specified by Attributes.
395
396 Caller should make sure BaseAddress and Length is at page boundary.
397
398 @param[in] BaseAddress The physical address that is the start address of a memory region.
399 @param[in] Length The size in bytes of the memory region.
400 @param[in] Attributes The bit mask of attributes to modify for the memory region.
401 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
402 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
403 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
404
405 @retval RETURN_SUCCESS The attributes were modified for the memory region.
406 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
407 BaseAddress and Length cannot be modified.
408 @retval RETURN_INVALID_PARAMETER Length is zero.
409 Attributes specified an illegal combination of attributes that
410 cannot be set together.
411 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
412 the memory resource range.
413 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
414 resource range specified by BaseAddress and Length.
415 The bit mask of attributes is not support for the memory resource
416 range specified by BaseAddress and Length.
417 **/
418 RETURN_STATUS
419 EFIAPI
420 ConvertMemoryPageAttributes (
421 IN PHYSICAL_ADDRESS BaseAddress,
422 IN UINT64 Length,
423 IN UINT64 Attributes,
424 IN BOOLEAN IsSet,
425 OUT BOOLEAN *IsSplitted, OPTIONAL
426 OUT BOOLEAN *IsModified OPTIONAL
427 )
428 {
429 UINT64 *PageEntry;
430 PAGE_ATTRIBUTE PageAttribute;
431 UINTN PageEntryLength;
432 PAGE_ATTRIBUTE SplitAttribute;
433 RETURN_STATUS Status;
434 BOOLEAN IsEntryModified;
435 EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress;
436
437 ASSERT (Attributes != 0);
438 ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0);
439
440 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
441 ASSERT ((Length & (SIZE_4KB - 1)) == 0);
442
443 if (Length == 0) {
444 return RETURN_INVALID_PARAMETER;
445 }
446
447 MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);
448 if (BaseAddress > MaximumSupportMemAddress) {
449 return RETURN_UNSUPPORTED;
450 }
451 if (Length > MaximumSupportMemAddress) {
452 return RETURN_UNSUPPORTED;
453 }
454 if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {
455 return RETURN_UNSUPPORTED;
456 }
457
458 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
459
460 if (IsSplitted != NULL) {
461 *IsSplitted = FALSE;
462 }
463 if (IsModified != NULL) {
464 *IsModified = FALSE;
465 }
466
467 //
468 // Below logic is to check 2M/4K page to make sure we do not waste memory.
469 //
470 while (Length != 0) {
471 PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute);
472 if (PageEntry == NULL) {
473 return RETURN_UNSUPPORTED;
474 }
475 PageEntryLength = PageAttributeToLength (PageAttribute);
476 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);
477 if (SplitAttribute == PageNone) {
478 ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);
479 if (IsEntryModified) {
480 if (IsModified != NULL) {
481 *IsModified = TRUE;
482 }
483 }
484 //
485 // Convert success, move to next
486 //
487 BaseAddress += PageEntryLength;
488 Length -= PageEntryLength;
489 } else {
490 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);
491 if (RETURN_ERROR (Status)) {
492 return RETURN_UNSUPPORTED;
493 }
494 if (IsSplitted != NULL) {
495 *IsSplitted = TRUE;
496 }
497 if (IsModified != NULL) {
498 *IsModified = TRUE;
499 }
500 //
501 // Just split current page
502 // Convert success in next around
503 //
504 }
505 }
506
507 return RETURN_SUCCESS;
508 }
509
510 /**
511 FlushTlb on current processor.
512
513 @param[in,out] Buffer Pointer to private data buffer.
514 **/
515 VOID
516 EFIAPI
517 FlushTlbOnCurrentProcessor (
518 IN OUT VOID *Buffer
519 )
520 {
521 CpuFlushTlb ();
522 }
523
524 /**
525 FlushTlb for all processors.
526 **/
527 VOID
528 FlushTlbForAll (
529 VOID
530 )
531 {
532 UINTN Index;
533
534 FlushTlbOnCurrentProcessor (NULL);
535
536 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
537 if (Index != gSmst->CurrentlyExecutingCpu) {
538 // Force to start up AP in blocking mode,
539 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);
540 // Do not check return status, because AP might not be present in some corner cases.
541 }
542 }
543 }
544
545 /**
546 This function sets the attributes for the memory region specified by BaseAddress and
547 Length from their current attributes to the attributes specified by Attributes.
548
549 @param[in] BaseAddress The physical address that is the start address of a memory region.
550 @param[in] Length The size in bytes of the memory region.
551 @param[in] Attributes The bit mask of attributes to set for the memory region.
552 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
553
554 @retval EFI_SUCCESS The attributes were set for the memory region.
555 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
556 BaseAddress and Length cannot be modified.
557 @retval EFI_INVALID_PARAMETER Length is zero.
558 Attributes specified an illegal combination of attributes that
559 cannot be set together.
560 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
561 the memory resource range.
562 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
563 resource range specified by BaseAddress and Length.
564 The bit mask of attributes is not support for the memory resource
565 range specified by BaseAddress and Length.
566
567 **/
568 EFI_STATUS
569 EFIAPI
570 SmmSetMemoryAttributesEx (
571 IN EFI_PHYSICAL_ADDRESS BaseAddress,
572 IN UINT64 Length,
573 IN UINT64 Attributes,
574 OUT BOOLEAN *IsSplitted OPTIONAL
575 )
576 {
577 EFI_STATUS Status;
578 BOOLEAN IsModified;
579
580 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);
581 if (!EFI_ERROR(Status)) {
582 if (IsModified) {
583 //
584 // Flush TLB as last step
585 //
586 FlushTlbForAll();
587 }
588 }
589
590 return Status;
591 }
592
593 /**
594 This function clears the attributes for the memory region specified by BaseAddress and
595 Length from their current attributes to the attributes specified by Attributes.
596
597 @param[in] BaseAddress The physical address that is the start address of a memory region.
598 @param[in] Length The size in bytes of the memory region.
599 @param[in] Attributes The bit mask of attributes to clear for the memory region.
600 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
601
602 @retval EFI_SUCCESS The attributes were cleared for the memory region.
603 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
604 BaseAddress and Length cannot be modified.
605 @retval EFI_INVALID_PARAMETER Length is zero.
606 Attributes specified an illegal combination of attributes that
607 cannot be cleared together.
608 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
609 the memory resource range.
610 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
611 resource range specified by BaseAddress and Length.
612 The bit mask of attributes is not supported for the memory resource
613 range specified by BaseAddress and Length.
614
615 **/
616 EFI_STATUS
617 EFIAPI
618 SmmClearMemoryAttributesEx (
619 IN EFI_PHYSICAL_ADDRESS BaseAddress,
620 IN UINT64 Length,
621 IN UINT64 Attributes,
622 OUT BOOLEAN *IsSplitted OPTIONAL
623 )
624 {
625 EFI_STATUS Status;
626 BOOLEAN IsModified;
627
628 Status = ConvertMemoryPageAttributes (BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);
629 if (!EFI_ERROR(Status)) {
630 if (IsModified) {
631 //
632 // Flush TLB as last step
633 //
634 FlushTlbForAll();
635 }
636 }
637
638 return Status;
639 }
640
641 /**
642 This function sets the attributes for the memory region specified by BaseAddress and
643 Length from their current attributes to the attributes specified by Attributes.
644
645 @param[in] BaseAddress The physical address that is the start address of a memory region.
646 @param[in] Length The size in bytes of the memory region.
647 @param[in] Attributes The bit mask of attributes to set for the memory region.
648
649 @retval EFI_SUCCESS The attributes were set for the memory region.
650 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
651 BaseAddress and Length cannot be modified.
652 @retval EFI_INVALID_PARAMETER Length is zero.
653 Attributes specified an illegal combination of attributes that
654 cannot be set together.
655 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
656 the memory resource range.
657 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
658 resource range specified by BaseAddress and Length.
659 The bit mask of attributes is not supported for the memory resource
660 range specified by BaseAddress and Length.
661
662 **/
663 EFI_STATUS
664 EFIAPI
665 SmmSetMemoryAttributes (
666 IN EFI_PHYSICAL_ADDRESS BaseAddress,
667 IN UINT64 Length,
668 IN UINT64 Attributes
669 )
670 {
671 return SmmSetMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);
672 }
673
674 /**
675 This function clears the attributes for the memory region specified by BaseAddress and
676 Length from their current attributes to the attributes specified by Attributes.
677
678 @param[in] BaseAddress The physical address that is the start address of a memory region.
679 @param[in] Length The size in bytes of the memory region.
680 @param[in] Attributes The bit mask of attributes to clear for the memory region.
681
682 @retval EFI_SUCCESS The attributes were cleared for the memory region.
683 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
684 BaseAddress and Length cannot be modified.
685 @retval EFI_INVALID_PARAMETER Length is zero.
686 Attributes specified an illegal combination of attributes that
687 cannot be cleared together.
688 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
689 the memory resource range.
690 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
691 resource range specified by BaseAddress and Length.
692 The bit mask of attributes is not supported for the memory resource
693 range specified by BaseAddress and Length.
694
695 **/
696 EFI_STATUS
697 EFIAPI
698 SmmClearMemoryAttributes (
699 IN EFI_PHYSICAL_ADDRESS BaseAddress,
700 IN UINT64 Length,
701 IN UINT64 Attributes
702 )
703 {
704 return SmmClearMemoryAttributesEx (BaseAddress, Length, Attributes, NULL);
705 }
706
707 /**
708 Set ShadowStack memory.
709
710 @param[in] Cr3 The page table base address.
711 @param[in] BaseAddress The physical address that is the start address of a memory region.
712 @param[in] Length The size in bytes of the memory region.
713
714 @retval EFI_SUCCESS The shadow stack memory is set.
715 **/
716 EFI_STATUS
717 SetShadowStack (
718 IN UINTN Cr3,
719 IN EFI_PHYSICAL_ADDRESS BaseAddress,
720 IN UINT64 Length
721 )
722 {
723 EFI_STATUS Status;
724
725 SetPageTableBase (Cr3);
726
727 Status = SmmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RO);
728
729 SetPageTableBase (0);
730
731 return Status;
732 }
733
734 /**
735 Set not present memory.
736
737 @param[in] Cr3 The page table base address.
738 @param[in] BaseAddress The physical address that is the start address of a memory region.
739 @param[in] Length The size in bytes of the memory region.
740
741 @retval EFI_SUCCESS The not present memory is set.
742 **/
743 EFI_STATUS
744 SetNotPresentPage (
745 IN UINTN Cr3,
746 IN EFI_PHYSICAL_ADDRESS BaseAddress,
747 IN UINT64 Length
748 )
749 {
750 EFI_STATUS Status;
751
752 SetPageTableBase (Cr3);
753
754 Status = SmmSetMemoryAttributes (BaseAddress, Length, EFI_MEMORY_RP);
755
756 SetPageTableBase (0);
757
758 return Status;
759 }
760
761 /**
762 Retrieves a pointer to the system configuration table from the SMM System Table
763 based on a specified GUID.
764
765 @param[in] TableGuid The pointer to table's GUID type.
766 @param[out] Table The pointer to the table associated with TableGuid in the EFI System Table.
767
768 @retval EFI_SUCCESS A configuration table matching TableGuid was found.
769 @retval EFI_NOT_FOUND A configuration table matching TableGuid could not be found.
770
771 **/
772 EFI_STATUS
773 EFIAPI
774 SmmGetSystemConfigurationTable (
775 IN EFI_GUID *TableGuid,
776 OUT VOID **Table
777 )
778 {
779 UINTN Index;
780
781 ASSERT (TableGuid != NULL);
782 ASSERT (Table != NULL);
783
784 *Table = NULL;
785 for (Index = 0; Index < gSmst->NumberOfTableEntries; Index++) {
786 if (CompareGuid (TableGuid, &(gSmst->SmmConfigurationTable[Index].VendorGuid))) {
787 *Table = gSmst->SmmConfigurationTable[Index].VendorTable;
788 return EFI_SUCCESS;
789 }
790 }
791
792 return EFI_NOT_FOUND;
793 }
794
795 /**
796 This function sets SMM save state buffer to be RW and XP.
797 **/
798 VOID
799 PatchSmmSaveStateMap (
800 VOID
801 )
802 {
803 UINTN Index;
804 UINTN TileCodeSize;
805 UINTN TileDataSize;
806 UINTN TileSize;
807
808 TileCodeSize = GetSmiHandlerSize ();
809 TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB);
810 TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP);
811 TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB);
812 TileSize = TileDataSize + TileCodeSize - 1;
813 TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize);
814
815 DEBUG ((DEBUG_INFO, "PatchSmmSaveStateMap:\n"));
816 for (Index = 0; Index < mMaxNumberOfCpus - 1; Index++) {
817 //
818 // Code
819 //
820 SmmSetMemoryAttributes (
821 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
822 TileCodeSize,
823 EFI_MEMORY_RO
824 );
825 SmmClearMemoryAttributes (
826 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET,
827 TileCodeSize,
828 EFI_MEMORY_XP
829 );
830
831 //
832 // Data
833 //
834 SmmClearMemoryAttributes (
835 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
836 TileSize - TileCodeSize,
837 EFI_MEMORY_RO
838 );
839 SmmSetMemoryAttributes (
840 mCpuHotPlugData.SmBase[Index] + SMM_HANDLER_OFFSET + TileCodeSize,
841 TileSize - TileCodeSize,
842 EFI_MEMORY_XP
843 );
844 }
845
846 //
847 // Code
848 //
849 SmmSetMemoryAttributes (
850 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
851 TileCodeSize,
852 EFI_MEMORY_RO
853 );
854 SmmClearMemoryAttributes (
855 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET,
856 TileCodeSize,
857 EFI_MEMORY_XP
858 );
859
860 //
861 // Data
862 //
863 SmmClearMemoryAttributes (
864 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
865 SIZE_32KB - TileCodeSize,
866 EFI_MEMORY_RO
867 );
868 SmmSetMemoryAttributes (
869 mCpuHotPlugData.SmBase[mMaxNumberOfCpus - 1] + SMM_HANDLER_OFFSET + TileCodeSize,
870 SIZE_32KB - TileCodeSize,
871 EFI_MEMORY_XP
872 );
873 }
874
875 /**
876 This function sets GDT/IDT buffer to be RO and XP.
877 **/
878 VOID
879 PatchGdtIdtMap (
880 VOID
881 )
882 {
883 EFI_PHYSICAL_ADDRESS BaseAddress;
884 UINTN Size;
885
886 //
887 // GDT
888 //
889 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - GDT:\n"));
890
891 BaseAddress = mGdtBuffer;
892 Size = ALIGN_VALUE(mGdtBufferSize, SIZE_4KB);
893 //
894 // The range should have been set to RO
895 // if it is allocated with EfiRuntimeServicesCode.
896 //
897 SmmSetMemoryAttributes (
898 BaseAddress,
899 Size,
900 EFI_MEMORY_XP
901 );
902
903 //
904 // IDT
905 //
906 DEBUG ((DEBUG_INFO, "PatchGdtIdtMap - IDT:\n"));
907
908 BaseAddress = gcSmiIdtr.Base;
909 Size = ALIGN_VALUE(gcSmiIdtr.Limit + 1, SIZE_4KB);
910 //
911 // The range should have been set to RO
912 // if it is allocated with EfiRuntimeServicesCode.
913 //
914 SmmSetMemoryAttributes (
915 BaseAddress,
916 Size,
917 EFI_MEMORY_XP
918 );
919 }
920
921 /**
922 This function sets memory attribute according to MemoryAttributesTable.
923 **/
924 VOID
925 SetMemMapAttributes (
926 VOID
927 )
928 {
929 EFI_MEMORY_DESCRIPTOR *MemoryMap;
930 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;
931 UINTN MemoryMapEntryCount;
932 UINTN DescriptorSize;
933 UINTN Index;
934 EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
935
936 SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
937 if (MemoryAttributesTable == NULL) {
938 DEBUG ((DEBUG_INFO, "MemoryAttributesTable - NULL\n"));
939 return ;
940 }
941
942 DEBUG ((DEBUG_INFO, "MemoryAttributesTable:\n"));
943 DEBUG ((DEBUG_INFO, " Version - 0x%08x\n", MemoryAttributesTable->Version));
944 DEBUG ((DEBUG_INFO, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries));
945 DEBUG ((DEBUG_INFO, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize));
946
947 MemoryMapEntryCount = MemoryAttributesTable->NumberOfEntries;
948 DescriptorSize = MemoryAttributesTable->DescriptorSize;
949 MemoryMapStart = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1);
950 MemoryMap = MemoryMapStart;
951 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
952 DEBUG ((DEBUG_INFO, "Entry (0x%x)\n", MemoryMap));
953 DEBUG ((DEBUG_INFO, " Type - 0x%x\n", MemoryMap->Type));
954 DEBUG ((DEBUG_INFO, " PhysicalStart - 0x%016lx\n", MemoryMap->PhysicalStart));
955 DEBUG ((DEBUG_INFO, " VirtualStart - 0x%016lx\n", MemoryMap->VirtualStart));
956 DEBUG ((DEBUG_INFO, " NumberOfPages - 0x%016lx\n", MemoryMap->NumberOfPages));
957 DEBUG ((DEBUG_INFO, " Attribute - 0x%016lx\n", MemoryMap->Attribute));
958 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
959 }
960
961 MemoryMap = MemoryMapStart;
962 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
963 DEBUG ((DEBUG_VERBOSE, "SetAttribute: Memory Entry - 0x%lx, 0x%x\n", MemoryMap->PhysicalStart, MemoryMap->NumberOfPages));
964 switch (MemoryMap->Type) {
965 case EfiRuntimeServicesCode:
966 SmmSetMemoryAttributes (
967 MemoryMap->PhysicalStart,
968 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
969 EFI_MEMORY_RO
970 );
971 break;
972 case EfiRuntimeServicesData:
973 SmmSetMemoryAttributes (
974 MemoryMap->PhysicalStart,
975 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
976 EFI_MEMORY_XP
977 );
978 break;
979 default:
980 SmmSetMemoryAttributes (
981 MemoryMap->PhysicalStart,
982 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
983 EFI_MEMORY_XP
984 );
985 break;
986 }
987 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, DescriptorSize);
988 }
989
990 PatchSmmSaveStateMap ();
991 PatchGdtIdtMap ();
992
993 return ;
994 }
995
996 /**
997 Sort memory map entries based upon PhysicalStart, from low to high.
998
999 @param MemoryMap A pointer to the buffer in which firmware places
1000 the current memory map.
1001 @param MemoryMapSize Size, in bytes, of the MemoryMap buffer.
1002 @param DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
1003 **/
1004 STATIC
1005 VOID
1006 SortMemoryMap (
1007 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
1008 IN UINTN MemoryMapSize,
1009 IN UINTN DescriptorSize
1010 )
1011 {
1012 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
1013 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
1014 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
1015 EFI_MEMORY_DESCRIPTOR TempMemoryMap;
1016
1017 MemoryMapEntry = MemoryMap;
1018 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1019 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + MemoryMapSize);
1020 while (MemoryMapEntry < MemoryMapEnd) {
1021 while (NextMemoryMapEntry < MemoryMapEnd) {
1022 if (MemoryMapEntry->PhysicalStart > NextMemoryMapEntry->PhysicalStart) {
1023 CopyMem (&TempMemoryMap, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
1024 CopyMem (MemoryMapEntry, NextMemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
1025 CopyMem (NextMemoryMapEntry, &TempMemoryMap, sizeof(EFI_MEMORY_DESCRIPTOR));
1026 }
1027
1028 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1029 }
1030
1031 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1032 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1033 }
1034 }
1035
1036 /**
1037 Return if a UEFI memory page should be marked as not present in SMM page table.
1038 If the memory map entries type is
1039 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1040 EfiUnusableMemory, EfiACPIReclaimMemory, return TRUE.
1041 Or return FALSE.
1042
1043 @param[in] MemoryMap A pointer to the memory descriptor.
1044
1045 @return TRUE The memory described will be marked as not present in SMM page table.
1046 @return FALSE The memory described will not be marked as not present in SMM page table.
1047 **/
1048 BOOLEAN
1049 IsUefiPageNotPresent (
1050 IN EFI_MEMORY_DESCRIPTOR *MemoryMap
1051 )
1052 {
1053 switch (MemoryMap->Type) {
1054 case EfiLoaderCode:
1055 case EfiLoaderData:
1056 case EfiBootServicesCode:
1057 case EfiBootServicesData:
1058 case EfiConventionalMemory:
1059 case EfiUnusableMemory:
1060 case EfiACPIReclaimMemory:
1061 return TRUE;
1062 default:
1063 return FALSE;
1064 }
1065 }
1066
1067 /**
1068 Merge continuous memory map entries whose type is
1069 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1070 EfiUnusableMemory, EfiACPIReclaimMemory, because the memory described by
1071 these entries will be set as NOT present in SMM page table.
1072
1073 @param[in, out] MemoryMap A pointer to the buffer in which firmware places
1074 the current memory map.
1075 @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the
1076 MemoryMap buffer. On input, this is the size of
1077 the current memory map. On output,
1078 it is the size of new memory map after merge.
1079 @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR.
1080 **/
1081 STATIC
1082 VOID
1083 MergeMemoryMapForNotPresentEntry (
1084 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,
1085 IN OUT UINTN *MemoryMapSize,
1086 IN UINTN DescriptorSize
1087 )
1088 {
1089 EFI_MEMORY_DESCRIPTOR *MemoryMapEntry;
1090 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;
1091 UINT64 MemoryBlockLength;
1092 EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry;
1093 EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry;
1094
1095 MemoryMapEntry = MemoryMap;
1096 NewMemoryMapEntry = MemoryMap;
1097 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *) ((UINT8 *) MemoryMap + *MemoryMapSize);
1098 while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) {
1099 CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof(EFI_MEMORY_DESCRIPTOR));
1100 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1101
1102 do {
1103 MemoryBlockLength = (UINT64) (EFI_PAGES_TO_SIZE((UINTN)MemoryMapEntry->NumberOfPages));
1104 if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) &&
1105 IsUefiPageNotPresent(MemoryMapEntry) && IsUefiPageNotPresent(NextMemoryMapEntry) &&
1106 ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) {
1107 MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
1108 if (NewMemoryMapEntry != MemoryMapEntry) {
1109 NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages;
1110 }
1111
1112 NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1113 continue;
1114 } else {
1115 MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize);
1116 break;
1117 }
1118 } while (TRUE);
1119
1120 MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize);
1121 NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize);
1122 }
1123
1124 *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap;
1125
1126 return ;
1127 }
1128
1129 /**
1130 This function caches the GCD memory map information.
1131 **/
1132 VOID
1133 GetGcdMemoryMap (
1134 VOID
1135 )
1136 {
1137 UINTN NumberOfDescriptors;
1138 EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemSpaceMap;
1139 EFI_STATUS Status;
1140 UINTN Index;
1141
1142 Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemSpaceMap);
1143 if (EFI_ERROR (Status)) {
1144 return ;
1145 }
1146
1147 mGcdMemNumberOfDesc = 0;
1148 for (Index = 0; Index < NumberOfDescriptors; Index++) {
1149 if (MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved &&
1150 (MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
1151 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)
1152 ) {
1153 mGcdMemNumberOfDesc++;
1154 }
1155 }
1156
1157 mGcdMemSpace = AllocateZeroPool (mGcdMemNumberOfDesc * sizeof (EFI_GCD_MEMORY_SPACE_DESCRIPTOR));
1158 ASSERT (mGcdMemSpace != NULL);
1159 if (mGcdMemSpace == NULL) {
1160 mGcdMemNumberOfDesc = 0;
1161 gBS->FreePool (MemSpaceMap);
1162 return ;
1163 }
1164
1165 mGcdMemNumberOfDesc = 0;
1166 for (Index = 0; Index < NumberOfDescriptors; Index++) {
1167 if (MemSpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeReserved &&
1168 (MemSpaceMap[Index].Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==
1169 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)
1170 ) {
1171 CopyMem (
1172 &mGcdMemSpace[mGcdMemNumberOfDesc],
1173 &MemSpaceMap[Index],
1174 sizeof(EFI_GCD_MEMORY_SPACE_DESCRIPTOR)
1175 );
1176 mGcdMemNumberOfDesc++;
1177 }
1178 }
1179
1180 gBS->FreePool (MemSpaceMap);
1181 }
1182
1183 /**
1184 Get UEFI MemoryAttributesTable.
1185 **/
1186 VOID
1187 GetUefiMemoryAttributesTable (
1188 VOID
1189 )
1190 {
1191 EFI_STATUS Status;
1192 EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable;
1193 UINTN MemoryAttributesTableSize;
1194
1195 Status = EfiGetSystemConfigurationTable (&gEfiMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable);
1196 if (!EFI_ERROR (Status) && (MemoryAttributesTable != NULL)) {
1197 MemoryAttributesTableSize = sizeof(EFI_MEMORY_ATTRIBUTES_TABLE) + MemoryAttributesTable->DescriptorSize * MemoryAttributesTable->NumberOfEntries;
1198 mUefiMemoryAttributesTable = AllocateCopyPool (MemoryAttributesTableSize, MemoryAttributesTable);
1199 ASSERT (mUefiMemoryAttributesTable != NULL);
1200 }
1201 }
1202
1203 /**
1204 This function caches the UEFI memory map information.
1205 **/
1206 VOID
1207 GetUefiMemoryMap (
1208 VOID
1209 )
1210 {
1211 EFI_STATUS Status;
1212 UINTN MapKey;
1213 UINT32 DescriptorVersion;
1214 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1215 UINTN UefiMemoryMapSize;
1216
1217 DEBUG ((DEBUG_INFO, "GetUefiMemoryMap\n"));
1218
1219 UefiMemoryMapSize = 0;
1220 MemoryMap = NULL;
1221 Status = gBS->GetMemoryMap (
1222 &UefiMemoryMapSize,
1223 MemoryMap,
1224 &MapKey,
1225 &mUefiDescriptorSize,
1226 &DescriptorVersion
1227 );
1228 ASSERT (Status == EFI_BUFFER_TOO_SMALL);
1229
1230 do {
1231 Status = gBS->AllocatePool (EfiBootServicesData, UefiMemoryMapSize, (VOID **)&MemoryMap);
1232 ASSERT (MemoryMap != NULL);
1233 if (MemoryMap == NULL) {
1234 return ;
1235 }
1236
1237 Status = gBS->GetMemoryMap (
1238 &UefiMemoryMapSize,
1239 MemoryMap,
1240 &MapKey,
1241 &mUefiDescriptorSize,
1242 &DescriptorVersion
1243 );
1244 if (EFI_ERROR (Status)) {
1245 gBS->FreePool (MemoryMap);
1246 MemoryMap = NULL;
1247 }
1248 } while (Status == EFI_BUFFER_TOO_SMALL);
1249
1250 if (MemoryMap == NULL) {
1251 return ;
1252 }
1253
1254 SortMemoryMap (MemoryMap, UefiMemoryMapSize, mUefiDescriptorSize);
1255 MergeMemoryMapForNotPresentEntry (MemoryMap, &UefiMemoryMapSize, mUefiDescriptorSize);
1256
1257 mUefiMemoryMapSize = UefiMemoryMapSize;
1258 mUefiMemoryMap = AllocateCopyPool (UefiMemoryMapSize, MemoryMap);
1259 ASSERT (mUefiMemoryMap != NULL);
1260
1261 gBS->FreePool (MemoryMap);
1262
1263 //
1264 // Get additional information from GCD memory map.
1265 //
1266 GetGcdMemoryMap ();
1267
1268 //
1269 // Get UEFI memory attributes table.
1270 //
1271 GetUefiMemoryAttributesTable ();
1272 }
1273
1274 /**
1275 This function sets UEFI memory attribute according to UEFI memory map.
1276
1277 The normal memory region is marked as not present, such as
1278 EfiLoaderCode/Data, EfiBootServicesCode/Data, EfiConventionalMemory,
1279 EfiUnusableMemory, EfiACPIReclaimMemory.
1280 **/
1281 VOID
1282 SetUefiMemMapAttributes (
1283 VOID
1284 )
1285 {
1286 EFI_STATUS Status;
1287 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1288 UINTN MemoryMapEntryCount;
1289 UINTN Index;
1290 EFI_MEMORY_DESCRIPTOR *Entry;
1291
1292 DEBUG ((DEBUG_INFO, "SetUefiMemMapAttributes\n"));
1293
1294 if (mUefiMemoryMap != NULL) {
1295 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
1296 MemoryMap = mUefiMemoryMap;
1297 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1298 if (IsUefiPageNotPresent(MemoryMap)) {
1299 Status = SmmSetMemoryAttributes (
1300 MemoryMap->PhysicalStart,
1301 EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
1302 EFI_MEMORY_RP
1303 );
1304 DEBUG ((
1305 DEBUG_INFO,
1306 "UefiMemory protection: 0x%lx - 0x%lx %r\n",
1307 MemoryMap->PhysicalStart,
1308 MemoryMap->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages),
1309 Status
1310 ));
1311 }
1312 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);
1313 }
1314 }
1315 //
1316 // Do not free mUefiMemoryMap, it will be checked in IsSmmCommBufferForbiddenAddress().
1317 //
1318
1319 //
1320 // Set untested memory as not present.
1321 //
1322 if (mGcdMemSpace != NULL) {
1323 for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
1324 Status = SmmSetMemoryAttributes (
1325 mGcdMemSpace[Index].BaseAddress,
1326 mGcdMemSpace[Index].Length,
1327 EFI_MEMORY_RP
1328 );
1329 DEBUG ((
1330 DEBUG_INFO,
1331 "GcdMemory protection: 0x%lx - 0x%lx %r\n",
1332 mGcdMemSpace[Index].BaseAddress,
1333 mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length,
1334 Status
1335 ));
1336 }
1337 }
1338 //
1339 // Do not free mGcdMemSpace, it will be checked in IsSmmCommBufferForbiddenAddress().
1340 //
1341
1342 //
1343 // Set UEFI runtime memory with EFI_MEMORY_RO as not present.
1344 //
1345 if (mUefiMemoryAttributesTable != NULL) {
1346 Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);
1347 for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {
1348 if (Entry->Type == EfiRuntimeServicesCode || Entry->Type == EfiRuntimeServicesData) {
1349 if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {
1350 Status = SmmSetMemoryAttributes (
1351 Entry->PhysicalStart,
1352 EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages),
1353 EFI_MEMORY_RP
1354 );
1355 DEBUG ((
1356 DEBUG_INFO,
1357 "UefiMemoryAttribute protection: 0x%lx - 0x%lx %r\n",
1358 Entry->PhysicalStart,
1359 Entry->PhysicalStart + (UINT64)EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages),
1360 Status
1361 ));
1362 }
1363 }
1364 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
1365 }
1366 }
1367 //
1368 // Do not free mUefiMemoryAttributesTable, it will be checked in IsSmmCommBufferForbiddenAddress().
1369 //
1370 }
1371
1372 /**
1373 Return if the Address is forbidden as SMM communication buffer.
1374
1375 @param[in] Address the address to be checked
1376
1377 @return TRUE The address is forbidden as SMM communication buffer.
1378 @return FALSE The address is allowed as SMM communication buffer.
1379 **/
1380 BOOLEAN
1381 IsSmmCommBufferForbiddenAddress (
1382 IN UINT64 Address
1383 )
1384 {
1385 EFI_MEMORY_DESCRIPTOR *MemoryMap;
1386 UINTN MemoryMapEntryCount;
1387 UINTN Index;
1388 EFI_MEMORY_DESCRIPTOR *Entry;
1389
1390 if (mUefiMemoryMap != NULL) {
1391 MemoryMap = mUefiMemoryMap;
1392 MemoryMapEntryCount = mUefiMemoryMapSize/mUefiDescriptorSize;
1393 for (Index = 0; Index < MemoryMapEntryCount; Index++) {
1394 if (IsUefiPageNotPresent (MemoryMap)) {
1395 if ((Address >= MemoryMap->PhysicalStart) &&
1396 (Address < MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE((UINTN)MemoryMap->NumberOfPages)) ) {
1397 return TRUE;
1398 }
1399 }
1400 MemoryMap = NEXT_MEMORY_DESCRIPTOR(MemoryMap, mUefiDescriptorSize);
1401 }
1402 }
1403
1404 if (mGcdMemSpace != NULL) {
1405 for (Index = 0; Index < mGcdMemNumberOfDesc; Index++) {
1406 if ((Address >= mGcdMemSpace[Index].BaseAddress) &&
1407 (Address < mGcdMemSpace[Index].BaseAddress + mGcdMemSpace[Index].Length) ) {
1408 return TRUE;
1409 }
1410 }
1411 }
1412
1413 if (mUefiMemoryAttributesTable != NULL) {
1414 Entry = (EFI_MEMORY_DESCRIPTOR *)(mUefiMemoryAttributesTable + 1);
1415 for (Index = 0; Index < mUefiMemoryAttributesTable->NumberOfEntries; Index++) {
1416 if (Entry->Type == EfiRuntimeServicesCode || Entry->Type == EfiRuntimeServicesData) {
1417 if ((Entry->Attribute & EFI_MEMORY_RO) != 0) {
1418 if ((Address >= Entry->PhysicalStart) &&
1419 (Address < Entry->PhysicalStart + LShiftU64 (Entry->NumberOfPages, EFI_PAGE_SHIFT))) {
1420 return TRUE;
1421 }
1422 Entry = NEXT_MEMORY_DESCRIPTOR (Entry, mUefiMemoryAttributesTable->DescriptorSize);
1423 }
1424 }
1425 }
1426 }
1427 return FALSE;
1428 }
1429
1430 /**
1431 This function set given attributes of the memory region specified by
1432 BaseAddress and Length.
1433
1434 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1435 @param BaseAddress The physical address that is the start address of
1436 a memory region.
1437 @param Length The size in bytes of the memory region.
1438 @param Attributes The bit mask of attributes to set for the memory
1439 region.
1440
1441 @retval EFI_SUCCESS The attributes were set for the memory region.
1442 @retval EFI_INVALID_PARAMETER Length is zero.
1443 Attributes specified an illegal combination of
1444 attributes that cannot be set together.
1445 @retval EFI_UNSUPPORTED The processor does not support one or more
1446 bytes of the memory resource range specified
1447 by BaseAddress and Length.
1448 The bit mask of attributes is not supported for
1449 the memory resource range specified by
1450 BaseAddress and Length.
1451
1452 **/
1453 EFI_STATUS
1454 EFIAPI
1455 EdkiiSmmSetMemoryAttributes (
1456 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1457 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1458 IN UINT64 Length,
1459 IN UINT64 Attributes
1460 )
1461 {
1462 return SmmSetMemoryAttributes (BaseAddress, Length, Attributes);
1463 }
1464
1465 /**
1466 This function clears given attributes of the memory region specified by
1467 BaseAddress and Length.
1468
1469 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1470 @param BaseAddress The physical address that is the start address of
1471 a memory region.
1472 @param Length The size in bytes of the memory region.
1473 @param Attributes The bit mask of attributes to clear for the memory
1474 region.
1475
1476 @retval EFI_SUCCESS The attributes were cleared for the memory region.
1477 @retval EFI_INVALID_PARAMETER Length is zero.
1478 Attributes specified an illegal combination of
1479 attributes that cannot be cleared together.
1480 @retval EFI_UNSUPPORTED The processor does not support one or more
1481 bytes of the memory resource range specified
1482 by BaseAddress and Length.
1483 The bit mask of attributes is not supported for
1484 the memory resource range specified by
1485 BaseAddress and Length.
1486
1487 **/
1488 EFI_STATUS
1489 EFIAPI
1490 EdkiiSmmClearMemoryAttributes (
1491 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1492 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1493 IN UINT64 Length,
1494 IN UINT64 Attributes
1495 )
1496 {
1497 return SmmClearMemoryAttributes (BaseAddress, Length, Attributes);
1498 }
1499
1500 /**
1501 This function retrieves the attributes of the memory region specified by
1502 BaseAddress and Length. If different attributes are got from different part
1503 of the memory region, EFI_NO_MAPPING will be returned.
1504
1505 @param This The EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL instance.
1506 @param BaseAddress The physical address that is the start address of
1507 a memory region.
1508 @param Length The size in bytes of the memory region.
1509 @param Attributes Pointer to attributes returned.
1510
1511 @retval EFI_SUCCESS The attributes got for the memory region.
1512 @retval EFI_INVALID_PARAMETER Length is zero.
1513 Attributes is NULL.
1514 @retval EFI_NO_MAPPING Attributes are not consistent cross the memory
1515 region.
1516 @retval EFI_UNSUPPORTED The processor does not support one or more
1517 bytes of the memory resource range specified
1518 by BaseAddress and Length.
1519
1520 **/
1521 EFI_STATUS
1522 EFIAPI
1523 EdkiiSmmGetMemoryAttributes (
1524 IN EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *This,
1525 IN EFI_PHYSICAL_ADDRESS BaseAddress,
1526 IN UINT64 Length,
1527 OUT UINT64 *Attributes
1528 )
1529 {
1530 EFI_PHYSICAL_ADDRESS Address;
1531 UINT64 *PageEntry;
1532 UINT64 MemAttr;
1533 PAGE_ATTRIBUTE PageAttr;
1534 INT64 Size;
1535
1536 if (Length < SIZE_4KB || Attributes == NULL) {
1537 return EFI_INVALID_PARAMETER;
1538 }
1539
1540 Size = (INT64)Length;
1541 MemAttr = (UINT64)-1;
1542
1543 do {
1544
1545 PageEntry = GetPageTableEntry (BaseAddress, &PageAttr);
1546 if (PageEntry == NULL || PageAttr == PageNone) {
1547 return EFI_UNSUPPORTED;
1548 }
1549
1550 //
1551 // If the memory range is cross page table boundary, make sure they
1552 // share the same attribute. Return EFI_NO_MAPPING if not.
1553 //
1554 *Attributes = GetAttributesFromPageEntry (PageEntry);
1555 if (MemAttr != (UINT64)-1 && *Attributes != MemAttr) {
1556 return EFI_NO_MAPPING;
1557 }
1558
1559 switch (PageAttr) {
1560 case Page4K:
1561 Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;
1562 Size -= (SIZE_4KB - (BaseAddress - Address));
1563 BaseAddress += (SIZE_4KB - (BaseAddress - Address));
1564 break;
1565
1566 case Page2M:
1567 Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;
1568 Size -= SIZE_2MB - (BaseAddress - Address);
1569 BaseAddress += SIZE_2MB - (BaseAddress - Address);
1570 break;
1571
1572 case Page1G:
1573 Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;
1574 Size -= SIZE_1GB - (BaseAddress - Address);
1575 BaseAddress += SIZE_1GB - (BaseAddress - Address);
1576 break;
1577
1578 default:
1579 return EFI_UNSUPPORTED;
1580 }
1581
1582 MemAttr = *Attributes;
1583
1584 } while (Size > 0);
1585
1586 return EFI_SUCCESS;
1587 }
1588