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