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