]> git.proxmox.com Git - mirror_edk2.git/blob - UefiCpuPkg/PiSmmCpuDxeSmm/SmmCpuMemoryManagement.c
UefiCpuPkg/PiSmmCpuDxeSmm: Remove mInternalCr3 in PiSmmCpuDxeSmm
[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 BOOLEAN mIsShadowStack = FALSE;
36 BOOLEAN m5LevelPagingNeeded = FALSE;
37
38 /**
39 Return length according to page attributes.
40
41 @param[in] PageAttributes The page attribute of the page entry.
42
43 @return The length of page entry.
44 **/
45 UINTN
46 PageAttributeToLength (
47 IN PAGE_ATTRIBUTE PageAttribute
48 )
49 {
50 UINTN Index;
51
52 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {
53 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
54 return (UINTN)mPageAttributeTable[Index].Length;
55 }
56 }
57
58 return 0;
59 }
60
61 /**
62 Return address mask according to page attributes.
63
64 @param[in] PageAttributes The page attribute of the page entry.
65
66 @return The address mask of page entry.
67 **/
68 UINTN
69 PageAttributeToMask (
70 IN PAGE_ATTRIBUTE PageAttribute
71 )
72 {
73 UINTN Index;
74
75 for (Index = 0; Index < sizeof (mPageAttributeTable)/sizeof (mPageAttributeTable[0]); Index++) {
76 if (PageAttribute == mPageAttributeTable[Index].Attribute) {
77 return (UINTN)mPageAttributeTable[Index].AddressMask;
78 }
79 }
80
81 return 0;
82 }
83
84 /**
85 Return page table entry to match the address.
86
87 @param[in] PageTableBase The page table base.
88 @param[in] Enable5LevelPaging If PML5 paging is enabled.
89 @param[in] Address The address to be checked.
90 @param[out] PageAttributes The page attribute of the page entry.
91
92 @return The page entry.
93 **/
94 VOID *
95 GetPageTableEntry (
96 IN UINTN PageTableBase,
97 IN BOOLEAN Enable5LevelPaging,
98 IN PHYSICAL_ADDRESS Address,
99 OUT PAGE_ATTRIBUTE *PageAttribute
100 )
101 {
102 UINTN Index1;
103 UINTN Index2;
104 UINTN Index3;
105 UINTN Index4;
106 UINTN Index5;
107 UINT64 *L1PageTable;
108 UINT64 *L2PageTable;
109 UINT64 *L3PageTable;
110 UINT64 *L4PageTable;
111 UINT64 *L5PageTable;
112
113 Index5 = ((UINTN)RShiftU64 (Address, 48)) & PAGING_PAE_INDEX_MASK;
114 Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK;
115 Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK;
116 Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK;
117 Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK;
118
119 if (sizeof (UINTN) == sizeof (UINT64)) {
120 if (Enable5LevelPaging) {
121 L5PageTable = (UINT64 *)PageTableBase;
122 if (L5PageTable[Index5] == 0) {
123 *PageAttribute = PageNone;
124 return NULL;
125 }
126
127 L4PageTable = (UINT64 *)(UINTN)(L5PageTable[Index5] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
128 } else {
129 L4PageTable = (UINT64 *)PageTableBase;
130 }
131
132 if (L4PageTable[Index4] == 0) {
133 *PageAttribute = PageNone;
134 return NULL;
135 }
136
137 L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
138 } else {
139 L3PageTable = (UINT64 *)PageTableBase;
140 }
141
142 if (L3PageTable[Index3] == 0) {
143 *PageAttribute = PageNone;
144 return NULL;
145 }
146
147 if ((L3PageTable[Index3] & IA32_PG_PS) != 0) {
148 // 1G
149 *PageAttribute = Page1G;
150 return &L3PageTable[Index3];
151 }
152
153 L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
154 if (L2PageTable[Index2] == 0) {
155 *PageAttribute = PageNone;
156 return NULL;
157 }
158
159 if ((L2PageTable[Index2] & IA32_PG_PS) != 0) {
160 // 2M
161 *PageAttribute = Page2M;
162 return &L2PageTable[Index2];
163 }
164
165 // 4k
166 L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64);
167 if ((L1PageTable[Index1] == 0) && (Address != 0)) {
168 *PageAttribute = PageNone;
169 return NULL;
170 }
171
172 *PageAttribute = Page4K;
173 return &L1PageTable[Index1];
174 }
175
176 /**
177 Return memory attributes of page entry.
178
179 @param[in] PageEntry The page entry.
180
181 @return Memory attributes of page entry.
182 **/
183 UINT64
184 GetAttributesFromPageEntry (
185 IN UINT64 *PageEntry
186 )
187 {
188 UINT64 Attributes;
189
190 Attributes = 0;
191 if ((*PageEntry & IA32_PG_P) == 0) {
192 Attributes |= EFI_MEMORY_RP;
193 }
194
195 if ((*PageEntry & IA32_PG_RW) == 0) {
196 Attributes |= EFI_MEMORY_RO;
197 }
198
199 if ((*PageEntry & IA32_PG_NX) != 0) {
200 Attributes |= EFI_MEMORY_XP;
201 }
202
203 return Attributes;
204 }
205
206 /**
207 Modify memory attributes of page entry.
208
209 @param[in] PageEntry The page entry.
210 @param[in] Attributes The bit mask of attributes to modify for the memory region.
211 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
212 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
213 **/
214 VOID
215 ConvertPageEntryAttribute (
216 IN UINT64 *PageEntry,
217 IN UINT64 Attributes,
218 IN BOOLEAN IsSet,
219 OUT BOOLEAN *IsModified
220 )
221 {
222 UINT64 CurrentPageEntry;
223 UINT64 NewPageEntry;
224
225 CurrentPageEntry = *PageEntry;
226 NewPageEntry = CurrentPageEntry;
227 if ((Attributes & EFI_MEMORY_RP) != 0) {
228 if (IsSet) {
229 NewPageEntry &= ~(UINT64)IA32_PG_P;
230 } else {
231 NewPageEntry |= IA32_PG_P;
232 }
233 }
234
235 if ((Attributes & EFI_MEMORY_RO) != 0) {
236 if (IsSet) {
237 NewPageEntry &= ~(UINT64)IA32_PG_RW;
238 if (mIsShadowStack) {
239 // Environment setup
240 // ReadOnly page need set Dirty bit for shadow stack
241 NewPageEntry |= IA32_PG_D;
242 // Clear user bit for supervisor shadow stack
243 NewPageEntry &= ~(UINT64)IA32_PG_U;
244 } else {
245 // Runtime update
246 // Clear dirty bit for non shadow stack, to protect RO page.
247 NewPageEntry &= ~(UINT64)IA32_PG_D;
248 }
249 } else {
250 NewPageEntry |= IA32_PG_RW;
251 }
252 }
253
254 if ((Attributes & EFI_MEMORY_XP) != 0) {
255 if (mXdSupported) {
256 if (IsSet) {
257 NewPageEntry |= IA32_PG_NX;
258 } else {
259 NewPageEntry &= ~IA32_PG_NX;
260 }
261 }
262 }
263
264 *PageEntry = NewPageEntry;
265 if (CurrentPageEntry != NewPageEntry) {
266 *IsModified = TRUE;
267 DEBUG ((DEBUG_VERBOSE, "ConvertPageEntryAttribute 0x%lx", CurrentPageEntry));
268 DEBUG ((DEBUG_VERBOSE, "->0x%lx\n", NewPageEntry));
269 } else {
270 *IsModified = FALSE;
271 }
272 }
273
274 /**
275 This function returns if there is need to split page entry.
276
277 @param[in] BaseAddress The base address to be checked.
278 @param[in] Length The length to be checked.
279 @param[in] PageEntry The page entry to be checked.
280 @param[in] PageAttribute The page attribute of the page entry.
281
282 @retval SplitAttributes on if there is need to split page entry.
283 **/
284 PAGE_ATTRIBUTE
285 NeedSplitPage (
286 IN PHYSICAL_ADDRESS BaseAddress,
287 IN UINT64 Length,
288 IN UINT64 *PageEntry,
289 IN PAGE_ATTRIBUTE PageAttribute
290 )
291 {
292 UINT64 PageEntryLength;
293
294 PageEntryLength = PageAttributeToLength (PageAttribute);
295
296 if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) {
297 return PageNone;
298 }
299
300 if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) {
301 return Page4K;
302 }
303
304 return Page2M;
305 }
306
307 /**
308 This function splits one page entry to small page entries.
309
310 @param[in] PageEntry The page entry to be splitted.
311 @param[in] PageAttribute The page attribute of the page entry.
312 @param[in] SplitAttribute How to split the page entry.
313
314 @retval RETURN_SUCCESS The page entry is splitted.
315 @retval RETURN_UNSUPPORTED The page entry does not support to be splitted.
316 @retval RETURN_OUT_OF_RESOURCES No resource to split page entry.
317 **/
318 RETURN_STATUS
319 SplitPage (
320 IN UINT64 *PageEntry,
321 IN PAGE_ATTRIBUTE PageAttribute,
322 IN PAGE_ATTRIBUTE SplitAttribute
323 )
324 {
325 UINT64 BaseAddress;
326 UINT64 *NewPageEntry;
327 UINTN Index;
328
329 ASSERT (PageAttribute == Page2M || PageAttribute == Page1G);
330
331 if (PageAttribute == Page2M) {
332 //
333 // Split 2M to 4K
334 //
335 ASSERT (SplitAttribute == Page4K);
336 if (SplitAttribute == Page4K) {
337 NewPageEntry = AllocatePageTableMemory (1);
338 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
339 if (NewPageEntry == NULL) {
340 return RETURN_OUT_OF_RESOURCES;
341 }
342
343 BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64;
344 for (Index = 0; Index < SIZE_4KB / sizeof (UINT64); Index++) {
345 NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS);
346 }
347
348 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
349 return RETURN_SUCCESS;
350 } else {
351 return RETURN_UNSUPPORTED;
352 }
353 } else if (PageAttribute == Page1G) {
354 //
355 // Split 1G to 2M
356 // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table.
357 //
358 ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K);
359 if (((SplitAttribute == Page2M) || (SplitAttribute == Page4K))) {
360 NewPageEntry = AllocatePageTableMemory (1);
361 DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry));
362 if (NewPageEntry == NULL) {
363 return RETURN_OUT_OF_RESOURCES;
364 }
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
371 (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS;
372 return RETURN_SUCCESS;
373 } else {
374 return RETURN_UNSUPPORTED;
375 }
376 } else {
377 return RETURN_UNSUPPORTED;
378 }
379 }
380
381 /**
382 This function modifies the page attributes for the memory region specified by BaseAddress and
383 Length from their current attributes to the attributes specified by Attributes.
384
385 Caller should make sure BaseAddress and Length is at page boundary.
386
387 @param[in] PageTableBase The page table base.
388 @param[in] EnablePML5Paging If PML5 paging is enabled.
389 @param[in] BaseAddress The physical address that is the start address of a memory region.
390 @param[in] Length The size in bytes of the memory region.
391 @param[in] Attributes The bit mask of attributes to modify for the memory region.
392 @param[in] IsSet TRUE means to set attributes. FALSE means to clear attributes.
393 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
394 @param[out] IsModified TRUE means page table modified. FALSE means page table not modified.
395
396 @retval RETURN_SUCCESS The attributes were modified for the memory region.
397 @retval RETURN_ACCESS_DENIED The attributes for the memory resource range specified by
398 BaseAddress and Length cannot be modified.
399 @retval RETURN_INVALID_PARAMETER Length is zero.
400 Attributes specified an illegal combination of attributes that
401 cannot be set together.
402 @retval RETURN_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
403 the memory resource range.
404 @retval RETURN_UNSUPPORTED The processor does not support one or more bytes of the memory
405 resource range specified by BaseAddress and Length.
406 The bit mask of attributes is not support for the memory resource
407 range specified by BaseAddress and Length.
408 **/
409 RETURN_STATUS
410 ConvertMemoryPageAttributes (
411 IN UINTN PageTableBase,
412 IN BOOLEAN EnablePML5Paging,
413 IN PHYSICAL_ADDRESS BaseAddress,
414 IN UINT64 Length,
415 IN UINT64 Attributes,
416 IN BOOLEAN IsSet,
417 OUT BOOLEAN *IsSplitted OPTIONAL,
418 OUT BOOLEAN *IsModified OPTIONAL
419 )
420 {
421 UINT64 *PageEntry;
422 PAGE_ATTRIBUTE PageAttribute;
423 UINTN PageEntryLength;
424 PAGE_ATTRIBUTE SplitAttribute;
425 RETURN_STATUS Status;
426 BOOLEAN IsEntryModified;
427 EFI_PHYSICAL_ADDRESS MaximumSupportMemAddress;
428
429 ASSERT (Attributes != 0);
430 ASSERT ((Attributes & ~EFI_MEMORY_ATTRIBUTE_MASK) == 0);
431
432 ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0);
433 ASSERT ((Length & (SIZE_4KB - 1)) == 0);
434
435 if (Length == 0) {
436 return RETURN_INVALID_PARAMETER;
437 }
438
439 MaximumSupportMemAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)(LShiftU64 (1, mPhysicalAddressBits) - 1);
440 if (BaseAddress > MaximumSupportMemAddress) {
441 return RETURN_UNSUPPORTED;
442 }
443
444 if (Length > MaximumSupportMemAddress) {
445 return RETURN_UNSUPPORTED;
446 }
447
448 if ((Length != 0) && (BaseAddress > MaximumSupportMemAddress - (Length - 1))) {
449 return RETURN_UNSUPPORTED;
450 }
451
452 // DEBUG ((DEBUG_ERROR, "ConvertMemoryPageAttributes(%x) - %016lx, %016lx, %02lx\n", IsSet, BaseAddress, Length, Attributes));
453
454 if (IsSplitted != NULL) {
455 *IsSplitted = FALSE;
456 }
457
458 if (IsModified != NULL) {
459 *IsModified = FALSE;
460 }
461
462 //
463 // Below logic is to check 2M/4K page to make sure we do not waste memory.
464 //
465 while (Length != 0) {
466 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttribute);
467 if (PageEntry == NULL) {
468 return RETURN_UNSUPPORTED;
469 }
470
471 PageEntryLength = PageAttributeToLength (PageAttribute);
472 SplitAttribute = NeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute);
473 if (SplitAttribute == PageNone) {
474 ConvertPageEntryAttribute (PageEntry, Attributes, IsSet, &IsEntryModified);
475 if (IsEntryModified) {
476 if (IsModified != NULL) {
477 *IsModified = TRUE;
478 }
479 }
480
481 //
482 // Convert success, move to next
483 //
484 BaseAddress += PageEntryLength;
485 Length -= PageEntryLength;
486 } else {
487 Status = SplitPage (PageEntry, PageAttribute, SplitAttribute);
488 if (RETURN_ERROR (Status)) {
489 return RETURN_UNSUPPORTED;
490 }
491
492 if (IsSplitted != NULL) {
493 *IsSplitted = TRUE;
494 }
495
496 if (IsModified != NULL) {
497 *IsModified = TRUE;
498 }
499
500 //
501 // Just split current page
502 // Convert success in next around
503 //
504 }
505 }
506
507 return RETURN_SUCCESS;
508 }
509
510 /**
511 FlushTlb on current processor.
512
513 @param[in,out] Buffer Pointer to private data buffer.
514 **/
515 VOID
516 EFIAPI
517 FlushTlbOnCurrentProcessor (
518 IN OUT VOID *Buffer
519 )
520 {
521 CpuFlushTlb ();
522 }
523
524 /**
525 FlushTlb for all processors.
526 **/
527 VOID
528 FlushTlbForAll (
529 VOID
530 )
531 {
532 UINTN Index;
533
534 FlushTlbOnCurrentProcessor (NULL);
535
536 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
537 if (Index != gSmst->CurrentlyExecutingCpu) {
538 // Force to start up AP in blocking mode,
539 SmmBlockingStartupThisAp (FlushTlbOnCurrentProcessor, Index, NULL);
540 // Do not check return status, because AP might not be present in some corner cases.
541 }
542 }
543 }
544
545 /**
546 This function sets the attributes for the memory region specified by BaseAddress and
547 Length from their current attributes to the attributes specified by Attributes.
548
549 @param[in] PageTableBase The page table base.
550 @param[in] EnablePML5Paging If PML5 paging is enabled.
551 @param[in] BaseAddress The physical address that is the start address of a memory region.
552 @param[in] Length The size in bytes of the memory region.
553 @param[in] Attributes The bit mask of attributes to set for the memory region.
554 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
555
556 @retval EFI_SUCCESS The attributes were set for the memory region.
557 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
558 BaseAddress and Length cannot be modified.
559 @retval EFI_INVALID_PARAMETER Length is zero.
560 Attributes specified an illegal combination of attributes that
561 cannot be set together.
562 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
563 the memory resource range.
564 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
565 resource range specified by BaseAddress and Length.
566 The bit mask of attributes is not support for the memory resource
567 range specified by BaseAddress and Length.
568
569 **/
570 EFI_STATUS
571 SmmSetMemoryAttributesEx (
572 IN UINTN PageTableBase,
573 IN BOOLEAN EnablePML5Paging,
574 IN EFI_PHYSICAL_ADDRESS BaseAddress,
575 IN UINT64 Length,
576 IN UINT64 Attributes,
577 OUT BOOLEAN *IsSplitted OPTIONAL
578 )
579 {
580 EFI_STATUS Status;
581 BOOLEAN IsModified;
582
583 Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, TRUE, IsSplitted, &IsModified);
584 if (!EFI_ERROR (Status)) {
585 if (IsModified) {
586 //
587 // Flush TLB as last step
588 //
589 FlushTlbForAll ();
590 }
591 }
592
593 return Status;
594 }
595
596 /**
597 This function clears the attributes for the memory region specified by BaseAddress and
598 Length from their current attributes to the attributes specified by Attributes.
599
600 @param[in] PageTableBase The page table base.
601 @param[in] EnablePML5Paging If PML5 paging is enabled.
602 @param[in] BaseAddress The physical address that is the start address of a memory region.
603 @param[in] Length The size in bytes of the memory region.
604 @param[in] Attributes The bit mask of attributes to clear for the memory region.
605 @param[out] IsSplitted TRUE means page table splitted. FALSE means page table not splitted.
606
607 @retval EFI_SUCCESS The attributes were cleared for the memory region.
608 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
609 BaseAddress and Length cannot be modified.
610 @retval EFI_INVALID_PARAMETER Length is zero.
611 Attributes specified an illegal combination of attributes that
612 cannot be cleared together.
613 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
614 the memory resource range.
615 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
616 resource range specified by BaseAddress and Length.
617 The bit mask of attributes is not supported for the memory resource
618 range specified by BaseAddress and Length.
619
620 **/
621 EFI_STATUS
622 SmmClearMemoryAttributesEx (
623 IN UINTN PageTableBase,
624 IN BOOLEAN EnablePML5Paging,
625 IN EFI_PHYSICAL_ADDRESS BaseAddress,
626 IN UINT64 Length,
627 IN UINT64 Attributes,
628 OUT BOOLEAN *IsSplitted OPTIONAL
629 )
630 {
631 EFI_STATUS Status;
632 BOOLEAN IsModified;
633
634 Status = ConvertMemoryPageAttributes (PageTableBase, EnablePML5Paging, BaseAddress, Length, Attributes, FALSE, IsSplitted, &IsModified);
635 if (!EFI_ERROR (Status)) {
636 if (IsModified) {
637 //
638 // Flush TLB as last step
639 //
640 FlushTlbForAll ();
641 }
642 }
643
644 return Status;
645 }
646
647 /**
648 This function sets the attributes for the memory region specified by BaseAddress and
649 Length from their current attributes to the attributes specified by Attributes.
650
651 @param[in] BaseAddress The physical address that is the start address of a memory region.
652 @param[in] Length The size in bytes of the memory region.
653 @param[in] Attributes The bit mask of attributes to set for the memory region.
654
655 @retval EFI_SUCCESS The attributes were set for the memory region.
656 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
657 BaseAddress and Length cannot be modified.
658 @retval EFI_INVALID_PARAMETER Length is zero.
659 Attributes specified an illegal combination of attributes that
660 cannot be set together.
661 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
662 the memory resource range.
663 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
664 resource range specified by BaseAddress and Length.
665 The bit mask of attributes is not supported for the memory resource
666 range specified by BaseAddress and Length.
667
668 **/
669 EFI_STATUS
670 SmmSetMemoryAttributes (
671 IN EFI_PHYSICAL_ADDRESS BaseAddress,
672 IN UINT64 Length,
673 IN UINT64 Attributes
674 )
675 {
676 IA32_CR4 Cr4;
677 UINTN PageTableBase;
678 BOOLEAN Enable5LevelPaging;
679
680 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
681 Cr4.UintN = AsmReadCr4 ();
682 Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
683 return SmmSetMemoryAttributesEx (PageTableBase, Enable5LevelPaging, BaseAddress, Length, Attributes, NULL);
684 }
685
686 /**
687 This function clears the attributes for the memory region specified by BaseAddress and
688 Length from their current attributes to the attributes specified by Attributes.
689
690 @param[in] BaseAddress The physical address that is the start address of a memory region.
691 @param[in] Length The size in bytes of the memory region.
692 @param[in] Attributes The bit mask of attributes to clear for the memory region.
693
694 @retval EFI_SUCCESS The attributes were cleared for the memory region.
695 @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by
696 BaseAddress and Length cannot be modified.
697 @retval EFI_INVALID_PARAMETER Length is zero.
698 Attributes specified an illegal combination of attributes that
699 cannot be cleared together.
700 @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of
701 the memory resource range.
702 @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory
703 resource range specified by BaseAddress and Length.
704 The bit mask of attributes is not supported for the memory resource
705 range specified by BaseAddress and Length.
706
707 **/
708 EFI_STATUS
709 SmmClearMemoryAttributes (
710 IN EFI_PHYSICAL_ADDRESS BaseAddress,
711 IN UINT64 Length,
712 IN UINT64 Attributes
713 )
714 {
715 IA32_CR4 Cr4;
716 UINTN PageTableBase;
717 BOOLEAN Enable5LevelPaging;
718
719 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
720 Cr4.UintN = AsmReadCr4 ();
721 Enable5LevelPaging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
722 return SmmClearMemoryAttributesEx (PageTableBase, Enable5LevelPaging, BaseAddress, Length, Attributes, NULL);
723 }
724
725 /**
726 Set ShadowStack memory.
727
728 @param[in] Cr3 The page table base address.
729 @param[in] BaseAddress The physical address that is the start address of a memory region.
730 @param[in] Length The size in bytes of the memory region.
731
732 @retval EFI_SUCCESS The shadow stack memory is set.
733 **/
734 EFI_STATUS
735 SetShadowStack (
736 IN UINTN Cr3,
737 IN EFI_PHYSICAL_ADDRESS BaseAddress,
738 IN UINT64 Length
739 )
740 {
741 EFI_STATUS Status;
742
743 mIsShadowStack = TRUE;
744 Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RO, NULL);
745 mIsShadowStack = FALSE;
746
747 return Status;
748 }
749
750 /**
751 Set not present memory.
752
753 @param[in] Cr3 The page table base address.
754 @param[in] BaseAddress The physical address that is the start address of a memory region.
755 @param[in] Length The size in bytes of the memory region.
756
757 @retval EFI_SUCCESS The not present memory is set.
758 **/
759 EFI_STATUS
760 SetNotPresentPage (
761 IN UINTN Cr3,
762 IN EFI_PHYSICAL_ADDRESS BaseAddress,
763 IN UINT64 Length
764 )
765 {
766 EFI_STATUS Status;
767
768 Status = SmmSetMemoryAttributesEx (Cr3, m5LevelPagingNeeded, BaseAddress, Length, EFI_MEMORY_RP, NULL);
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 UINTN PageTableBase;
1562 BOOLEAN EnablePML5Paging;
1563 IA32_CR4 Cr4;
1564
1565 if ((Length < SIZE_4KB) || (Attributes == NULL)) {
1566 return EFI_INVALID_PARAMETER;
1567 }
1568
1569 Size = (INT64)Length;
1570 MemAttr = (UINT64)-1;
1571
1572 PageTableBase = AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64;
1573 Cr4.UintN = AsmReadCr4 ();
1574 EnablePML5Paging = (BOOLEAN)(Cr4.Bits.LA57 == 1);
1575
1576 do {
1577 PageEntry = GetPageTableEntry (PageTableBase, EnablePML5Paging, BaseAddress, &PageAttr);
1578 if ((PageEntry == NULL) || (PageAttr == PageNone)) {
1579 return EFI_UNSUPPORTED;
1580 }
1581
1582 //
1583 // If the memory range is cross page table boundary, make sure they
1584 // share the same attribute. Return EFI_NO_MAPPING if not.
1585 //
1586 *Attributes = GetAttributesFromPageEntry (PageEntry);
1587 if ((MemAttr != (UINT64)-1) && (*Attributes != MemAttr)) {
1588 return EFI_NO_MAPPING;
1589 }
1590
1591 switch (PageAttr) {
1592 case Page4K:
1593 Address = *PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64;
1594 Size -= (SIZE_4KB - (BaseAddress - Address));
1595 BaseAddress += (SIZE_4KB - (BaseAddress - Address));
1596 break;
1597
1598 case Page2M:
1599 Address = *PageEntry & ~mAddressEncMask & PAGING_2M_ADDRESS_MASK_64;
1600 Size -= SIZE_2MB - (BaseAddress - Address);
1601 BaseAddress += SIZE_2MB - (BaseAddress - Address);
1602 break;
1603
1604 case Page1G:
1605 Address = *PageEntry & ~mAddressEncMask & PAGING_1G_ADDRESS_MASK_64;
1606 Size -= SIZE_1GB - (BaseAddress - Address);
1607 BaseAddress += SIZE_1GB - (BaseAddress - Address);
1608 break;
1609
1610 default:
1611 return EFI_UNSUPPORTED;
1612 }
1613
1614 MemAttr = *Attributes;
1615 } while (Size > 0);
1616
1617 return EFI_SUCCESS;
1618 }