1 /** @file
2   SMM Memory page management functions.
3 
4   Copyright (c) 2009 - 2018, Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #include "PiSmmCore.h"
10 #include <Library/SmmServicesTableLib.h>
11 
12 #define TRUNCATE_TO_PAGES(a)  ((a) >> EFI_PAGE_SHIFT)
13 
14 LIST_ENTRY  mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);
15 
16 //
17 // For GetMemoryMap()
18 //
19 
20 #define MEMORY_MAP_SIGNATURE   SIGNATURE_32('m','m','a','p')
21 typedef struct {
22   UINTN           Signature;
23   LIST_ENTRY      Link;
24 
25   BOOLEAN         FromStack;
26   EFI_MEMORY_TYPE Type;
27   UINT64          Start;
28   UINT64          End;
29 
30 } MEMORY_MAP;
31 
32 LIST_ENTRY        gMemoryMap  = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap);
33 
34 
35 #define MAX_MAP_DEPTH 6
36 
37 ///
38 /// mMapDepth - depth of new descriptor stack
39 ///
40 UINTN         mMapDepth = 0;
41 ///
42 /// mMapStack - space to use as temp storage to build new map descriptors
43 ///
44 MEMORY_MAP    mMapStack[MAX_MAP_DEPTH];
45 UINTN         mFreeMapStack = 0;
46 ///
47 /// This list maintain the free memory map list
48 ///
49 LIST_ENTRY   mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList);
50 
51 /**
52   Allocates pages from the memory map.
53 
54   @param[in]   Type                   The type of allocation to perform.
55   @param[in]   MemoryType             The type of memory to turn the allocated pages
56                                       into.
57   @param[in]   NumberOfPages          The number of pages to allocate.
58   @param[out]  Memory                 A pointer to receive the base allocated memory
59                                       address.
60   @param[in]   AddRegion              If this memory is new added region.
61   @param[in]   NeedGuard              Flag to indicate Guard page is needed
62                                       or not
63 
64   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
65   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
66   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
67   @retval EFI_SUCCESS            Pages successfully allocated.
68 
69 **/
70 EFI_STATUS
71 SmmInternalAllocatePagesEx (
72   IN  EFI_ALLOCATE_TYPE     Type,
73   IN  EFI_MEMORY_TYPE       MemoryType,
74   IN  UINTN                 NumberOfPages,
75   OUT EFI_PHYSICAL_ADDRESS  *Memory,
76   IN  BOOLEAN               AddRegion,
77   IN  BOOLEAN               NeedGuard
78   );
79 
80 /**
81   Internal function.  Deque a descriptor entry from the mFreeMemoryMapEntryList.
82   If the list is emtry, then allocate a new page to refuel the list.
83   Please Note this algorithm to allocate the memory map descriptor has a property
84   that the memory allocated for memory entries always grows, and will never really be freed.
85 
86   @return The Memory map descriptor dequeued from the mFreeMemoryMapEntryList
87 
88 **/
89 MEMORY_MAP *
AllocateMemoryMapEntry(VOID)90 AllocateMemoryMapEntry (
91   VOID
92   )
93 {
94   EFI_PHYSICAL_ADDRESS   Mem;
95   EFI_STATUS             Status;
96   MEMORY_MAP*            FreeDescriptorEntries;
97   MEMORY_MAP*            Entry;
98   UINTN                  Index;
99 
100   //DEBUG((DEBUG_INFO, "AllocateMemoryMapEntry\n"));
101 
102   if (IsListEmpty (&mFreeMemoryMapEntryList)) {
103     //DEBUG((DEBUG_INFO, "mFreeMemoryMapEntryList is empty\n"));
104     //
105     // The list is empty, to allocate one page to refuel the list
106     //
107     Status = SmmInternalAllocatePagesEx (
108                AllocateAnyPages,
109                EfiRuntimeServicesData,
110                EFI_SIZE_TO_PAGES (RUNTIME_PAGE_ALLOCATION_GRANULARITY),
111                &Mem,
112                TRUE,
113                FALSE
114                );
115     ASSERT_EFI_ERROR (Status);
116     if(!EFI_ERROR (Status)) {
117       FreeDescriptorEntries = (MEMORY_MAP *)(UINTN)Mem;
118       //DEBUG((DEBUG_INFO, "New FreeDescriptorEntries - 0x%x\n", FreeDescriptorEntries));
119       //
120       // Enqueue the free memory map entries into the list
121       //
122       for (Index = 0; Index< RUNTIME_PAGE_ALLOCATION_GRANULARITY / sizeof(MEMORY_MAP); Index++) {
123         FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE;
124         InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link);
125       }
126     } else {
127       return NULL;
128     }
129   }
130   //
131   // dequeue the first descriptor from the list
132   //
133   Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
134   RemoveEntryList (&Entry->Link);
135 
136   return Entry;
137 }
138 
139 
140 /**
141   Internal function.  Moves any memory descriptors that are on the
142   temporary descriptor stack to heap.
143 
144 **/
145 VOID
CoreFreeMemoryMapStack(VOID)146 CoreFreeMemoryMapStack (
147   VOID
148   )
149 {
150   MEMORY_MAP      *Entry;
151 
152   //
153   // If already freeing the map stack, then return
154   //
155   if (mFreeMapStack != 0) {
156     ASSERT (FALSE);
157     return ;
158   }
159 
160   //
161   // Move the temporary memory descriptor stack into pool
162   //
163   mFreeMapStack += 1;
164 
165   while (mMapDepth != 0) {
166     //
167     // Deque an memory map entry from mFreeMemoryMapEntryList
168     //
169     Entry = AllocateMemoryMapEntry ();
170     ASSERT (Entry);
171 
172     //
173     // Update to proper entry
174     //
175     mMapDepth -= 1;
176 
177     if (mMapStack[mMapDepth].Link.ForwardLink != NULL) {
178 
179       CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP));
180       Entry->FromStack = FALSE;
181 
182       //
183       // Move this entry to general memory
184       //
185       InsertTailList (&mMapStack[mMapDepth].Link, &Entry->Link);
186       RemoveEntryList (&mMapStack[mMapDepth].Link);
187       mMapStack[mMapDepth].Link.ForwardLink = NULL;
188     }
189   }
190 
191   mFreeMapStack -= 1;
192 }
193 
194 /**
195   Insert new entry from memory map.
196 
197   @param[in]  Link       The old memory map entry to be linked.
198   @param[in]  Start      The start address of new memory map entry.
199   @param[in]  End        The end address of new memory map entry.
200   @param[in]  Type       The type of new memory map entry.
201   @param[in]  Next       If new entry is inserted to the next of old entry.
202   @param[in]  AddRegion  If this memory is new added region.
203 **/
204 VOID
InsertNewEntry(IN LIST_ENTRY * Link,IN UINT64 Start,IN UINT64 End,IN EFI_MEMORY_TYPE Type,IN BOOLEAN Next,IN BOOLEAN AddRegion)205 InsertNewEntry (
206   IN LIST_ENTRY      *Link,
207   IN UINT64          Start,
208   IN UINT64          End,
209   IN EFI_MEMORY_TYPE Type,
210   IN BOOLEAN         Next,
211   IN BOOLEAN         AddRegion
212   )
213 {
214   MEMORY_MAP  *Entry;
215 
216   Entry = &mMapStack[mMapDepth];
217   mMapDepth += 1;
218   ASSERT (mMapDepth < MAX_MAP_DEPTH);
219   Entry->FromStack = TRUE;
220 
221   Entry->Signature = MEMORY_MAP_SIGNATURE;
222   Entry->Type = Type;
223   Entry->Start = Start;
224   Entry->End = End;
225   if (Next) {
226     InsertHeadList (Link, &Entry->Link);
227   } else {
228     InsertTailList (Link, &Entry->Link);
229   }
230 }
231 
232 /**
233   Remove old entry from memory map.
234 
235   @param[in] Entry Memory map entry to be removed.
236 **/
237 VOID
RemoveOldEntry(IN MEMORY_MAP * Entry)238 RemoveOldEntry (
239   IN MEMORY_MAP  *Entry
240   )
241 {
242   RemoveEntryList (&Entry->Link);
243   Entry->Link.ForwardLink = NULL;
244 
245   if (!Entry->FromStack) {
246     InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);
247   }
248 }
249 
250 /**
251   Update SMM memory map entry.
252 
253   @param[in]  Type                   The type of allocation to perform.
254   @param[in]  Memory                 The base of memory address.
255   @param[in]  NumberOfPages          The number of pages to allocate.
256   @param[in]  AddRegion              If this memory is new added region.
257 **/
258 VOID
ConvertSmmMemoryMapEntry(IN EFI_MEMORY_TYPE Type,IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages,IN BOOLEAN AddRegion)259 ConvertSmmMemoryMapEntry (
260   IN EFI_MEMORY_TYPE       Type,
261   IN EFI_PHYSICAL_ADDRESS  Memory,
262   IN UINTN                 NumberOfPages,
263   IN BOOLEAN               AddRegion
264   )
265 {
266   LIST_ENTRY               *Link;
267   MEMORY_MAP               *Entry;
268   MEMORY_MAP               *NextEntry;
269   LIST_ENTRY               *NextLink;
270   MEMORY_MAP               *PreviousEntry;
271   LIST_ENTRY               *PreviousLink;
272   EFI_PHYSICAL_ADDRESS     Start;
273   EFI_PHYSICAL_ADDRESS     End;
274 
275   Start = Memory;
276   End = Memory + EFI_PAGES_TO_SIZE(NumberOfPages) - 1;
277 
278   //
279   // Exclude memory region
280   //
281   Link = gMemoryMap.ForwardLink;
282   while (Link != &gMemoryMap) {
283     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
284     Link  = Link->ForwardLink;
285 
286     //
287     // ---------------------------------------------------
288     // |  +----------+   +------+   +------+   +------+  |
289     // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|---
290     //    +----------+ ^ +------+   +------+   +------+
291     //                 |
292     //              +------+
293     //              |EntryX|
294     //              +------+
295     //
296     if (Entry->Start > End) {
297       if ((Entry->Start == End + 1) && (Entry->Type == Type)) {
298         Entry->Start = Start;
299         return ;
300       }
301       InsertNewEntry (
302         &Entry->Link,
303         Start,
304         End,
305         Type,
306         FALSE,
307         AddRegion
308         );
309       return ;
310     }
311 
312     if ((Entry->Start <= Start) && (Entry->End >= End)) {
313       if (Entry->Type != Type) {
314         if (Entry->Start < Start) {
315           //
316           // ---------------------------------------------------
317           // |  +----------+   +------+   +------+   +------+  |
318           // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|---
319           //    +----------+   +------+ ^ +------+   +------+
320           //                            |
321           //                         +------+
322           //                         |EntryA|
323           //                         +------+
324           //
325           InsertNewEntry (
326             &Entry->Link,
327             Entry->Start,
328             Start - 1,
329             Entry->Type,
330             FALSE,
331             AddRegion
332             );
333         }
334         if (Entry->End > End) {
335           //
336           // ---------------------------------------------------
337           // |  +----------+   +------+   +------+   +------+  |
338           // ---|gMemoryMep|---|Entry1|---|EntryX|---|Entry3|---
339           //    +----------+   +------+   +------+ ^ +------+
340           //                                       |
341           //                                    +------+
342           //                                    |EntryZ|
343           //                                    +------+
344           //
345           InsertNewEntry (
346             &Entry->Link,
347             End + 1,
348             Entry->End,
349             Entry->Type,
350             TRUE,
351             AddRegion
352             );
353         }
354         //
355         // Update this node
356         //
357         Entry->Start = Start;
358         Entry->End = End;
359         Entry->Type = Type;
360 
361         //
362         // Check adjacent
363         //
364         NextLink = Entry->Link.ForwardLink;
365         if (NextLink != &gMemoryMap) {
366           NextEntry = CR (NextLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
367           //
368           // ---------------------------------------------------
369           // |  +----------+   +------+   +-----------------+  |
370           // ---|gMemoryMep|---|Entry1|---|EntryX     Entry3|---
371           //    +----------+   +------+   +-----------------+
372           //
373           if ((Entry->Type == NextEntry->Type) && (Entry->End + 1 == NextEntry->Start)) {
374             Entry->End = NextEntry->End;
375             RemoveOldEntry (NextEntry);
376           }
377         }
378         PreviousLink = Entry->Link.BackLink;
379         if (PreviousLink != &gMemoryMap) {
380           PreviousEntry = CR (PreviousLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
381           //
382           // ---------------------------------------------------
383           // |  +----------+   +-----------------+   +------+  |
384           // ---|gMemoryMep|---|Entry1     EntryX|---|Entry3|---
385           //    +----------+   +-----------------+   +------+
386           //
387           if ((PreviousEntry->Type == Entry->Type) && (PreviousEntry->End + 1 == Entry->Start)) {
388             PreviousEntry->End = Entry->End;
389             RemoveOldEntry (Entry);
390           }
391         }
392       }
393       return ;
394     }
395   }
396 
397   //
398   // ---------------------------------------------------
399   // |  +----------+   +------+   +------+   +------+  |
400   // ---|gMemoryMep|---|Entry1|---|Entry2|---|Entry3|---
401   //    +----------+   +------+   +------+   +------+ ^
402   //                                                  |
403   //                                               +------+
404   //                                               |EntryX|
405   //                                               +------+
406   //
407   Link = gMemoryMap.BackLink;
408   if (Link != &gMemoryMap) {
409     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
410     if ((Entry->End + 1 == Start) && (Entry->Type == Type)) {
411       Entry->End = End;
412       return ;
413     }
414   }
415   InsertNewEntry (
416     &gMemoryMap,
417     Start,
418     End,
419     Type,
420     FALSE,
421     AddRegion
422     );
423   return ;
424 }
425 
426 /**
427   Return the count of Smm memory map entry.
428 
429   @return The count of Smm memory map entry.
430 **/
431 UINTN
GetSmmMemoryMapEntryCount(VOID)432 GetSmmMemoryMapEntryCount (
433   VOID
434   )
435 {
436   LIST_ENTRY               *Link;
437   UINTN                    Count;
438 
439   Count = 0;
440   Link = gMemoryMap.ForwardLink;
441   while (Link != &gMemoryMap) {
442     Link  = Link->ForwardLink;
443     Count++;
444   }
445   return Count;
446 }
447 
448 
449 
450 /**
451   Internal Function. Allocate n pages from given free page node.
452 
453   @param  Pages                  The free page node.
454   @param  NumberOfPages          Number of pages to be allocated.
455   @param  MaxAddress             Request to allocate memory below this address.
456 
457   @return Memory address of allocated pages.
458 
459 **/
460 UINTN
InternalAllocPagesOnOneNode(IN OUT FREE_PAGE_LIST * Pages,IN UINTN NumberOfPages,IN UINTN MaxAddress)461 InternalAllocPagesOnOneNode (
462   IN OUT FREE_PAGE_LIST  *Pages,
463   IN     UINTN           NumberOfPages,
464   IN     UINTN           MaxAddress
465   )
466 {
467   UINTN           Top;
468   UINTN           Bottom;
469   FREE_PAGE_LIST  *Node;
470 
471   Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);
472   if (Top > Pages->NumberOfPages) {
473     Top = Pages->NumberOfPages;
474   }
475   Bottom = Top - NumberOfPages;
476 
477   if (Top < Pages->NumberOfPages) {
478     Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));
479     Node->NumberOfPages = Pages->NumberOfPages - Top;
480     InsertHeadList (&Pages->Link, &Node->Link);
481   }
482 
483   if (Bottom > 0) {
484     Pages->NumberOfPages = Bottom;
485   } else {
486     RemoveEntryList (&Pages->Link);
487   }
488 
489   return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);
490 }
491 
492 /**
493   Internal Function. Allocate n pages from free page list below MaxAddress.
494 
495   @param  FreePageList           The free page node.
496   @param  NumberOfPages          Number of pages to be allocated.
497   @param  MaxAddress             Request to allocate memory below this address.
498 
499   @return Memory address of allocated pages.
500 
501 **/
502 UINTN
InternalAllocMaxAddress(IN OUT LIST_ENTRY * FreePageList,IN UINTN NumberOfPages,IN UINTN MaxAddress)503 InternalAllocMaxAddress (
504   IN OUT LIST_ENTRY  *FreePageList,
505   IN     UINTN       NumberOfPages,
506   IN     UINTN       MaxAddress
507   )
508 {
509   LIST_ENTRY      *Node;
510   FREE_PAGE_LIST  *Pages;
511 
512   for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {
513     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
514     if (Pages->NumberOfPages >= NumberOfPages &&
515         (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {
516       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);
517     }
518   }
519   return (UINTN)(-1);
520 }
521 
522 /**
523   Internal Function. Allocate n pages from free page list at given address.
524 
525   @param  FreePageList           The free page node.
526   @param  NumberOfPages          Number of pages to be allocated.
527   @param  MaxAddress             Request to allocate memory below this address.
528 
529   @return Memory address of allocated pages.
530 
531 **/
532 UINTN
InternalAllocAddress(IN OUT LIST_ENTRY * FreePageList,IN UINTN NumberOfPages,IN UINTN Address)533 InternalAllocAddress (
534   IN OUT LIST_ENTRY  *FreePageList,
535   IN     UINTN       NumberOfPages,
536   IN     UINTN       Address
537   )
538 {
539   UINTN           EndAddress;
540   LIST_ENTRY      *Node;
541   FREE_PAGE_LIST  *Pages;
542 
543   if ((Address & EFI_PAGE_MASK) != 0) {
544     return ~Address;
545   }
546 
547   EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);
548   for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {
549     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
550     if ((UINTN)Pages <= Address) {
551       if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {
552         break;
553       }
554       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);
555     }
556   }
557   return ~Address;
558 }
559 
560 /**
561   Allocates pages from the memory map.
562 
563   @param[in]   Type                   The type of allocation to perform.
564   @param[in]   MemoryType             The type of memory to turn the allocated pages
565                                       into.
566   @param[in]   NumberOfPages          The number of pages to allocate.
567   @param[out]  Memory                 A pointer to receive the base allocated memory
568                                       address.
569   @param[in]   AddRegion              If this memory is new added region.
570   @param[in]   NeedGuard              Flag to indicate Guard page is needed
571                                       or not
572 
573   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
574   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
575   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
576   @retval EFI_SUCCESS            Pages successfully allocated.
577 
578 **/
579 EFI_STATUS
SmmInternalAllocatePagesEx(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory,IN BOOLEAN AddRegion,IN BOOLEAN NeedGuard)580 SmmInternalAllocatePagesEx (
581   IN  EFI_ALLOCATE_TYPE     Type,
582   IN  EFI_MEMORY_TYPE       MemoryType,
583   IN  UINTN                 NumberOfPages,
584   OUT EFI_PHYSICAL_ADDRESS  *Memory,
585   IN  BOOLEAN               AddRegion,
586   IN  BOOLEAN               NeedGuard
587   )
588 {
589   UINTN  RequestedAddress;
590 
591   if (MemoryType != EfiRuntimeServicesCode &&
592       MemoryType != EfiRuntimeServicesData) {
593     return EFI_INVALID_PARAMETER;
594   }
595 
596   if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {
597     return EFI_OUT_OF_RESOURCES;
598   }
599 
600   //
601   // We don't track memory type in SMM
602   //
603   RequestedAddress = (UINTN)*Memory;
604   switch (Type) {
605     case AllocateAnyPages:
606       RequestedAddress = (UINTN)(-1);
607     case AllocateMaxAddress:
608       if (NeedGuard) {
609         *Memory = InternalAllocMaxAddressWithGuard (
610                       &mSmmMemoryMap,
611                       NumberOfPages,
612                       RequestedAddress,
613                       MemoryType
614                       );
615         if (*Memory == (UINTN)-1) {
616           return EFI_OUT_OF_RESOURCES;
617         } else {
618           ASSERT (VerifyMemoryGuard (*Memory, NumberOfPages) == TRUE);
619           return EFI_SUCCESS;
620         }
621       }
622 
623       *Memory = InternalAllocMaxAddress (
624                   &mSmmMemoryMap,
625                   NumberOfPages,
626                   RequestedAddress
627                   );
628       if (*Memory == (UINTN)-1) {
629         return EFI_OUT_OF_RESOURCES;
630       }
631       break;
632     case AllocateAddress:
633       *Memory = InternalAllocAddress (
634                   &mSmmMemoryMap,
635                   NumberOfPages,
636                   RequestedAddress
637                   );
638       if (*Memory != RequestedAddress) {
639         return EFI_NOT_FOUND;
640       }
641       break;
642     default:
643       return EFI_INVALID_PARAMETER;
644   }
645 
646   //
647   // Update SmmMemoryMap here.
648   //
649   ConvertSmmMemoryMapEntry (MemoryType, *Memory, NumberOfPages, AddRegion);
650   if (!AddRegion) {
651     CoreFreeMemoryMapStack();
652   }
653 
654   return EFI_SUCCESS;
655 }
656 
657 /**
658   Allocates pages from the memory map.
659 
660   @param[in]   Type                   The type of allocation to perform.
661   @param[in]   MemoryType             The type of memory to turn the allocated pages
662                                       into.
663   @param[in]   NumberOfPages          The number of pages to allocate.
664   @param[out]  Memory                 A pointer to receive the base allocated memory
665                                       address.
666   @param[in]   NeedGuard              Flag to indicate Guard page is needed
667                                       or not
668 
669   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
670   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
671   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
672   @retval EFI_SUCCESS            Pages successfully allocated.
673 
674 **/
675 EFI_STATUS
676 EFIAPI
SmmInternalAllocatePages(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory,IN BOOLEAN NeedGuard)677 SmmInternalAllocatePages (
678   IN  EFI_ALLOCATE_TYPE     Type,
679   IN  EFI_MEMORY_TYPE       MemoryType,
680   IN  UINTN                 NumberOfPages,
681   OUT EFI_PHYSICAL_ADDRESS  *Memory,
682   IN  BOOLEAN               NeedGuard
683   )
684 {
685   return SmmInternalAllocatePagesEx (Type, MemoryType, NumberOfPages, Memory,
686                                      FALSE, NeedGuard);
687 }
688 
689 /**
690   Allocates pages from the memory map.
691 
692   @param  Type                   The type of allocation to perform.
693   @param  MemoryType             The type of memory to turn the allocated pages
694                                  into.
695   @param  NumberOfPages          The number of pages to allocate.
696   @param  Memory                 A pointer to receive the base allocated memory
697                                  address.
698 
699   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
700   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
701   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
702   @retval EFI_SUCCESS            Pages successfully allocated.
703 
704 **/
705 EFI_STATUS
706 EFIAPI
SmmAllocatePages(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory)707 SmmAllocatePages (
708   IN  EFI_ALLOCATE_TYPE     Type,
709   IN  EFI_MEMORY_TYPE       MemoryType,
710   IN  UINTN                 NumberOfPages,
711   OUT EFI_PHYSICAL_ADDRESS  *Memory
712   )
713 {
714   EFI_STATUS  Status;
715   BOOLEAN     NeedGuard;
716 
717   NeedGuard = IsPageTypeToGuard (MemoryType, Type);
718   Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory,
719                                      NeedGuard);
720   if (!EFI_ERROR (Status)) {
721     SmmCoreUpdateProfile (
722       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
723       MemoryProfileActionAllocatePages,
724       MemoryType,
725       EFI_PAGES_TO_SIZE (NumberOfPages),
726       (VOID *) (UINTN) *Memory,
727       NULL
728       );
729   }
730   return Status;
731 }
732 
733 /**
734   Internal Function. Merge two adjacent nodes.
735 
736   @param  First             The first of two nodes to merge.
737 
738   @return Pointer to node after merge (if success) or pointer to next node (if fail).
739 
740 **/
741 FREE_PAGE_LIST *
InternalMergeNodes(IN FREE_PAGE_LIST * First)742 InternalMergeNodes (
743   IN FREE_PAGE_LIST  *First
744   )
745 {
746   FREE_PAGE_LIST  *Next;
747 
748   Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
749   ASSERT (
750     TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
751 
752   if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
753     First->NumberOfPages += Next->NumberOfPages;
754     RemoveEntryList (&Next->Link);
755     Next = First;
756   }
757   return Next;
758 }
759 
760 /**
761   Frees previous allocated pages.
762 
763   @param[in]  Memory                 Base address of memory being freed.
764   @param[in]  NumberOfPages          The number of pages to free.
765   @param[in]  AddRegion              If this memory is new added region.
766 
767   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
768   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
769   @return EFI_SUCCESS            Pages successfully freed.
770 
771 **/
772 EFI_STATUS
SmmInternalFreePagesEx(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages,IN BOOLEAN AddRegion)773 SmmInternalFreePagesEx (
774   IN EFI_PHYSICAL_ADDRESS  Memory,
775   IN UINTN                 NumberOfPages,
776   IN BOOLEAN               AddRegion
777   )
778 {
779   LIST_ENTRY      *Node;
780   FREE_PAGE_LIST  *Pages;
781 
782   if (((Memory & EFI_PAGE_MASK) != 0) || (Memory == 0) || (NumberOfPages == 0)) {
783     return EFI_INVALID_PARAMETER;
784   }
785 
786   Pages = NULL;
787   Node = mSmmMemoryMap.ForwardLink;
788   while (Node != &mSmmMemoryMap) {
789     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
790     if (Memory < (UINTN)Pages) {
791       break;
792     }
793     Node = Node->ForwardLink;
794   }
795 
796   if (Node != &mSmmMemoryMap &&
797       Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
798     return EFI_INVALID_PARAMETER;
799   }
800 
801   if (Node->BackLink != &mSmmMemoryMap) {
802     Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
803     if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
804       return EFI_INVALID_PARAMETER;
805     }
806   }
807 
808   Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
809   Pages->NumberOfPages = NumberOfPages;
810   InsertTailList (Node, &Pages->Link);
811 
812   if (Pages->Link.BackLink != &mSmmMemoryMap) {
813     Pages = InternalMergeNodes (
814               BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
815               );
816   }
817 
818   if (Node != &mSmmMemoryMap) {
819     InternalMergeNodes (Pages);
820   }
821 
822   //
823   // Update SmmMemoryMap here.
824   //
825   ConvertSmmMemoryMapEntry (EfiConventionalMemory, Memory, NumberOfPages, AddRegion);
826   if (!AddRegion) {
827     CoreFreeMemoryMapStack();
828   }
829 
830   return EFI_SUCCESS;
831 }
832 
833 /**
834   Frees previous allocated pages.
835 
836   @param[in]  Memory                 Base address of memory being freed.
837   @param[in]  NumberOfPages          The number of pages to free.
838   @param[in]  IsGuarded              Is the memory to free guarded or not.
839 
840   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
841   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
842   @return EFI_SUCCESS            Pages successfully freed.
843 
844 **/
845 EFI_STATUS
846 EFIAPI
SmmInternalFreePages(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages,IN BOOLEAN IsGuarded)847 SmmInternalFreePages (
848   IN EFI_PHYSICAL_ADDRESS  Memory,
849   IN UINTN                 NumberOfPages,
850   IN BOOLEAN               IsGuarded
851   )
852 {
853   if (IsGuarded) {
854     return SmmInternalFreePagesExWithGuard (Memory, NumberOfPages, FALSE);
855   }
856   return SmmInternalFreePagesEx (Memory, NumberOfPages, FALSE);
857 }
858 
859 /**
860   Check whether the input range is in memory map.
861 
862   @param  Memory                 Base address of memory being inputed.
863   @param  NumberOfPages          The number of pages.
864 
865   @retval TRUE   In memory map.
866   @retval FALSE  Not in memory map.
867 
868 **/
869 BOOLEAN
InMemMap(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages)870 InMemMap (
871   IN EFI_PHYSICAL_ADDRESS  Memory,
872   IN UINTN                 NumberOfPages
873   )
874 {
875   LIST_ENTRY               *Link;
876   MEMORY_MAP               *Entry;
877   EFI_PHYSICAL_ADDRESS     Last;
878 
879   Last = Memory + EFI_PAGES_TO_SIZE (NumberOfPages) - 1;
880 
881   Link = gMemoryMap.ForwardLink;
882   while (Link != &gMemoryMap) {
883     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
884     Link  = Link->ForwardLink;
885 
886     if ((Entry->Start <= Memory) && (Entry->End >= Last)) {
887       return TRUE;
888     }
889   }
890 
891   return FALSE;
892 }
893 
894 /**
895   Frees previous allocated pages.
896 
897   @param  Memory                 Base address of memory being freed.
898   @param  NumberOfPages          The number of pages to free.
899 
900   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
901   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
902   @return EFI_SUCCESS            Pages successfully freed.
903 
904 **/
905 EFI_STATUS
906 EFIAPI
SmmFreePages(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages)907 SmmFreePages (
908   IN EFI_PHYSICAL_ADDRESS  Memory,
909   IN UINTN                 NumberOfPages
910   )
911 {
912   EFI_STATUS  Status;
913   BOOLEAN     IsGuarded;
914 
915   if (!InMemMap(Memory, NumberOfPages)) {
916     return EFI_NOT_FOUND;
917   }
918 
919   IsGuarded = IsHeapGuardEnabled () && IsMemoryGuarded (Memory);
920   Status = SmmInternalFreePages (Memory, NumberOfPages, IsGuarded);
921   if (!EFI_ERROR (Status)) {
922     SmmCoreUpdateProfile (
923       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
924       MemoryProfileActionFreePages,
925       EfiMaxMemoryType,
926       EFI_PAGES_TO_SIZE (NumberOfPages),
927       (VOID *) (UINTN) Memory,
928       NULL
929       );
930   }
931   return Status;
932 }
933 
934 /**
935   Add free SMRAM region for use by memory service.
936 
937   @param  MemBase                Base address of memory region.
938   @param  MemLength              Length of the memory region.
939   @param  Type                   Memory type.
940   @param  Attributes             Memory region state.
941 
942 **/
943 VOID
SmmAddMemoryRegion(IN EFI_PHYSICAL_ADDRESS MemBase,IN UINT64 MemLength,IN EFI_MEMORY_TYPE Type,IN UINT64 Attributes)944 SmmAddMemoryRegion (
945   IN  EFI_PHYSICAL_ADDRESS  MemBase,
946   IN  UINT64                MemLength,
947   IN  EFI_MEMORY_TYPE       Type,
948   IN  UINT64                Attributes
949   )
950 {
951   UINTN  AlignedMemBase;
952 
953   //
954   // Add EfiRuntimeServicesData for memory regions that is already allocated, needs testing, or needs ECC initialization
955   //
956   if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
957     Type = EfiRuntimeServicesData;
958   } else {
959     Type = EfiConventionalMemory;
960   }
961 
962   DEBUG ((DEBUG_INFO, "SmmAddMemoryRegion\n"));
963   DEBUG ((DEBUG_INFO, "  MemBase    - 0x%lx\n", MemBase));
964   DEBUG ((DEBUG_INFO, "  MemLength  - 0x%lx\n", MemLength));
965   DEBUG ((DEBUG_INFO, "  Type       - 0x%x\n", Type));
966   DEBUG ((DEBUG_INFO, "  Attributes - 0x%lx\n", Attributes));
967 
968   //
969   // Align range on an EFI_PAGE_SIZE boundary
970   //
971   AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
972   MemLength -= AlignedMemBase - MemBase;
973   if (Type == EfiConventionalMemory) {
974     SmmInternalFreePagesEx (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE);
975   } else {
976     ConvertSmmMemoryMapEntry (EfiRuntimeServicesData, AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE);
977   }
978 
979   CoreFreeMemoryMapStack ();
980 }
981 
982 /**
983   This function returns a copy of the current memory map. The map is an array of
984   memory descriptors, each of which describes a contiguous block of memory.
985 
986   @param[in, out]  MemoryMapSize          A pointer to the size, in bytes, of the
987                                           MemoryMap buffer. On input, this is the size of
988                                           the buffer allocated by the caller.  On output,
989                                           it is the size of the buffer returned by the
990                                           firmware  if the buffer was large enough, or the
991                                           size of the buffer needed  to contain the map if
992                                           the buffer was too small.
993   @param[in, out]  MemoryMap              A pointer to the buffer in which firmware places
994                                           the current memory map.
995   @param[out]      MapKey                 A pointer to the location in which firmware
996                                           returns the key for the current memory map.
997   @param[out]      DescriptorSize         A pointer to the location in which firmware
998                                           returns the size, in bytes, of an individual
999                                           EFI_MEMORY_DESCRIPTOR.
1000   @param[out]      DescriptorVersion      A pointer to the location in which firmware
1001                                           returns the version number associated with the
1002                                           EFI_MEMORY_DESCRIPTOR.
1003 
1004   @retval EFI_SUCCESS            The memory map was returned in the MemoryMap
1005                                  buffer.
1006   @retval EFI_BUFFER_TOO_SMALL   The MemoryMap buffer was too small. The current
1007                                  buffer size needed to hold the memory map is
1008                                  returned in MemoryMapSize.
1009   @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.
1010 
1011 **/
1012 EFI_STATUS
1013 EFIAPI
SmmCoreGetMemoryMap(IN OUT UINTN * MemoryMapSize,IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap,OUT UINTN * MapKey,OUT UINTN * DescriptorSize,OUT UINT32 * DescriptorVersion)1014 SmmCoreGetMemoryMap (
1015   IN OUT UINTN                  *MemoryMapSize,
1016   IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,
1017   OUT UINTN                     *MapKey,
1018   OUT UINTN                     *DescriptorSize,
1019   OUT UINT32                    *DescriptorVersion
1020   )
1021 {
1022   UINTN                    Count;
1023   LIST_ENTRY               *Link;
1024   MEMORY_MAP               *Entry;
1025   UINTN                    Size;
1026   UINTN                    BufferSize;
1027 
1028   Size = sizeof (EFI_MEMORY_DESCRIPTOR);
1029 
1030   //
1031   // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will
1032   // prevent people from having pointer math bugs in their code.
1033   // now you have to use *DescriptorSize to make things work.
1034   //
1035   Size += sizeof(UINT64) - (Size % sizeof (UINT64));
1036 
1037   if (DescriptorSize != NULL) {
1038     *DescriptorSize = Size;
1039   }
1040 
1041   if (DescriptorVersion != NULL) {
1042     *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION;
1043   }
1044 
1045   Count = GetSmmMemoryMapEntryCount ();
1046   BufferSize = Size * Count;
1047   if (*MemoryMapSize < BufferSize) {
1048     *MemoryMapSize = BufferSize;
1049     return EFI_BUFFER_TOO_SMALL;
1050   }
1051 
1052   *MemoryMapSize = BufferSize;
1053   if (MemoryMap == NULL) {
1054     return EFI_INVALID_PARAMETER;
1055   }
1056 
1057   ZeroMem (MemoryMap, BufferSize);
1058   Link = gMemoryMap.ForwardLink;
1059   while (Link != &gMemoryMap) {
1060     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
1061     Link  = Link->ForwardLink;
1062 
1063     MemoryMap->Type           = Entry->Type;
1064     MemoryMap->PhysicalStart  = Entry->Start;
1065     MemoryMap->NumberOfPages  = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);
1066 
1067     MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);
1068   }
1069 
1070   return EFI_SUCCESS;
1071 }
1072