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