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