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