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