• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   SMM Memory page management functions.
3 
4   Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5   This program and the accompanying materials are licensed and made available
6   under the terms and conditions of the BSD License which accompanies this
7   distribution.  The full text of the license may be found at
8   http://opensource.org/licenses/bsd-license.php
9 
10   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "PiSmmCore.h"
16 #include <Library/SmmServicesTableLib.h>
17 
18 #define TRUNCATE_TO_PAGES(a)  ((a) >> EFI_PAGE_SHIFT)
19 
20 LIST_ENTRY  mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);
21 
22 //
23 // For GetMemoryMap()
24 //
25 
26 #define MEMORY_MAP_SIGNATURE   SIGNATURE_32('m','m','a','p')
27 typedef struct {
28   UINTN           Signature;
29   LIST_ENTRY      Link;
30 
31   BOOLEAN         FromStack;
32   EFI_MEMORY_TYPE Type;
33   UINT64          Start;
34   UINT64          End;
35 
36 } MEMORY_MAP;
37 
38 LIST_ENTRY        gMemoryMap  = INITIALIZE_LIST_HEAD_VARIABLE (gMemoryMap);
39 
40 
41 #define MAX_MAP_DEPTH 6
42 
43 ///
44 /// mMapDepth - depth of new descriptor stack
45 ///
46 UINTN         mMapDepth = 0;
47 ///
48 /// mMapStack - space to use as temp storage to build new map descriptors
49 ///
50 MEMORY_MAP    mMapStack[MAX_MAP_DEPTH];
51 UINTN         mFreeMapStack = 0;
52 ///
53 /// This list maintain the free memory map list
54 ///
55 LIST_ENTRY   mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList);
56 
57 /**
58   Allocates pages from the memory map.
59 
60   @param[in]   Type                   The type of allocation to perform.
61   @param[in]   MemoryType             The type of memory to turn the allocated pages
62                                       into.
63   @param[in]   NumberOfPages          The number of pages to allocate.
64   @param[out]  Memory                 A pointer to receive the base allocated memory
65                                       address.
66   @param[in]   AddRegion              If this memory is new added region.
67 
68   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
69   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
70   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
71   @retval EFI_SUCCESS            Pages successfully allocated.
72 
73 **/
74 EFI_STATUS
75 SmmInternalAllocatePagesEx (
76   IN  EFI_ALLOCATE_TYPE     Type,
77   IN  EFI_MEMORY_TYPE       MemoryType,
78   IN  UINTN                 NumberOfPages,
79   OUT EFI_PHYSICAL_ADDRESS  *Memory,
80   IN  BOOLEAN               AddRegion
81   );
82 
83 /**
84   Internal function.  Deque a descriptor entry from the mFreeMemoryMapEntryList.
85   If the list is emtry, then allocate a new page to refuel the list.
86   Please Note this algorithm to allocate the memory map descriptor has a property
87   that the memory allocated for memory entries always grows, and will never really be freed.
88 
89   @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList
90 
91 **/
92 MEMORY_MAP *
AllocateMemoryMapEntry(VOID)93 AllocateMemoryMapEntry (
94   VOID
95   )
96 {
97   EFI_PHYSICAL_ADDRESS   Mem;
98   EFI_STATUS             Status;
99   MEMORY_MAP*            FreeDescriptorEntries;
100   MEMORY_MAP*            Entry;
101   UINTN                  Index;
102 
103   //DEBUG((DEBUG_INFO, "AllocateMemoryMapEntry\n"));
104 
105   if (IsListEmpty (&mFreeMemoryMapEntryList)) {
106     //DEBUG((DEBUG_INFO, "mFreeMemoryMapEntryList is empty\n"));
107     //
108     // The list is empty, to allocate one page to refuel the list
109     //
110     Status = SmmInternalAllocatePagesEx (
111                AllocateAnyPages,
112                EfiRuntimeServicesData,
113                EFI_SIZE_TO_PAGES(DEFAULT_PAGE_ALLOCATION),
114                &Mem,
115                TRUE
116                );
117     ASSERT_EFI_ERROR (Status);
118     if(!EFI_ERROR (Status)) {
119       FreeDescriptorEntries = (MEMORY_MAP *)(UINTN)Mem;
120       //DEBUG((DEBUG_INFO, "New FreeDescriptorEntries - 0x%x\n", FreeDescriptorEntries));
121       //
122       // Enque the free memmory map entries into the list
123       //
124       for (Index = 0; Index< DEFAULT_PAGE_ALLOCATION / sizeof(MEMORY_MAP); Index++) {
125         FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE;
126         InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link);
127       }
128     } else {
129       return NULL;
130     }
131   }
132   //
133   // dequeue the first descriptor from the list
134   //
135   Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
136   RemoveEntryList (&Entry->Link);
137 
138   return Entry;
139 }
140 
141 
142 /**
143   Internal function.  Moves any memory descriptors that are on the
144   temporary descriptor stack to heap.
145 
146 **/
147 VOID
CoreFreeMemoryMapStack(VOID)148 CoreFreeMemoryMapStack (
149   VOID
150   )
151 {
152   MEMORY_MAP      *Entry;
153 
154   //
155   // If already freeing the map stack, then return
156   //
157   if (mFreeMapStack != 0) {
158     ASSERT (FALSE);
159     return ;
160   }
161 
162   //
163   // Move the temporary memory descriptor stack into pool
164   //
165   mFreeMapStack += 1;
166 
167   while (mMapDepth != 0) {
168     //
169     // Deque an memory map entry from mFreeMemoryMapEntryList
170     //
171     Entry = AllocateMemoryMapEntry ();
172     ASSERT (Entry);
173 
174     //
175     // Update to proper entry
176     //
177     mMapDepth -= 1;
178 
179     if (mMapStack[mMapDepth].Link.ForwardLink != NULL) {
180 
181       CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP));
182       Entry->FromStack = FALSE;
183 
184       //
185       // Move this entry to general memory
186       //
187       InsertTailList (&mMapStack[mMapDepth].Link, &Entry->Link);
188       RemoveEntryList (&mMapStack[mMapDepth].Link);
189       mMapStack[mMapDepth].Link.ForwardLink = NULL;
190     }
191   }
192 
193   mFreeMapStack -= 1;
194 }
195 
196 /**
197   Insert new entry from memory map.
198 
199   @param[in]  Link       The old memory map entry to be linked.
200   @param[in]  Start      The start address of new memory map entry.
201   @param[in]  End        The end address of new memory map entry.
202   @param[in]  Type       The type of new memory map entry.
203   @param[in]  Next       If new entry is inserted to the next of old entry.
204   @param[in]  AddRegion  If this memory is new added region.
205 **/
206 VOID
InsertNewEntry(IN LIST_ENTRY * Link,IN UINT64 Start,IN UINT64 End,IN EFI_MEMORY_TYPE Type,IN BOOLEAN Next,IN BOOLEAN AddRegion)207 InsertNewEntry (
208   IN LIST_ENTRY      *Link,
209   IN UINT64          Start,
210   IN UINT64          End,
211   IN EFI_MEMORY_TYPE Type,
212   IN BOOLEAN         Next,
213   IN BOOLEAN         AddRegion
214   )
215 {
216   MEMORY_MAP  *Entry;
217 
218   Entry = &mMapStack[mMapDepth];
219   mMapDepth += 1;
220   ASSERT (mMapDepth < MAX_MAP_DEPTH);
221   Entry->FromStack = TRUE;
222 
223   Entry->Signature = MEMORY_MAP_SIGNATURE;
224   Entry->Type = Type;
225   Entry->Start = Start;
226   Entry->End = End;
227   if (Next) {
228     InsertHeadList (Link, &Entry->Link);
229   } else {
230     InsertTailList (Link, &Entry->Link);
231   }
232 }
233 
234 /**
235   Remove old entry from memory map.
236 
237   @param[in] Entry Memory map entry to be removed.
238 **/
239 VOID
RemoveOldEntry(IN MEMORY_MAP * Entry)240 RemoveOldEntry (
241   IN MEMORY_MAP  *Entry
242   )
243 {
244   RemoveEntryList (&Entry->Link);
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   Dump Smm memory map entry.
450 **/
451 VOID
DumpSmmMemoryMapEntry(VOID)452 DumpSmmMemoryMapEntry (
453   VOID
454   )
455 {
456   LIST_ENTRY               *Link;
457   MEMORY_MAP               *Entry;
458   EFI_PHYSICAL_ADDRESS     Last;
459 
460   Last = 0;
461   DEBUG ((DEBUG_INFO, "DumpSmmMemoryMapEntry:\n"));
462   Link = gMemoryMap.ForwardLink;
463   while (Link != &gMemoryMap) {
464     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
465     Link  = Link->ForwardLink;
466 
467     if ((Last != 0) && (Last != (UINT64)-1)) {
468       if (Last + 1 != Entry->Start) {
469         Last = (UINT64)-1;
470       } else {
471         Last = Entry->End;
472       }
473     } else if (Last == 0) {
474       Last = Entry->End;
475     }
476 
477     DEBUG ((DEBUG_INFO, "Entry (Link - 0x%x)\n", &Entry->Link));
478     DEBUG ((DEBUG_INFO, "  Signature         - 0x%x\n", Entry->Signature));
479     DEBUG ((DEBUG_INFO, "  Link.ForwardLink  - 0x%x\n", Entry->Link.ForwardLink));
480     DEBUG ((DEBUG_INFO, "  Link.BackLink     - 0x%x\n", Entry->Link.BackLink));
481     DEBUG ((DEBUG_INFO, "  Type              - 0x%x\n", Entry->Type));
482     DEBUG ((DEBUG_INFO, "  Start             - 0x%016lx\n", Entry->Start));
483     DEBUG ((DEBUG_INFO, "  End               - 0x%016lx\n", Entry->End));
484   }
485 
486   ASSERT (Last != (UINT64)-1);
487 }
488 
489 /**
490   Dump Smm memory map.
491 **/
492 VOID
DumpSmmMemoryMap(VOID)493 DumpSmmMemoryMap (
494   VOID
495   )
496 {
497   LIST_ENTRY      *Node;
498   FREE_PAGE_LIST  *Pages;
499 
500   DEBUG ((DEBUG_INFO, "DumpSmmMemoryMap\n"));
501 
502   Pages = NULL;
503   Node = mSmmMemoryMap.ForwardLink;
504   while (Node != &mSmmMemoryMap) {
505     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
506     DEBUG ((DEBUG_INFO, "Pages - 0x%x\n", Pages));
507     DEBUG ((DEBUG_INFO, "Pages->NumberOfPages - 0x%x\n", Pages->NumberOfPages));
508     Node = Node->ForwardLink;
509   }
510 }
511 
512 /**
513   Check if a Smm base~length is in Smm memory map.
514 
515   @param[in] Base   The base address of Smm memory to be checked.
516   @param[in] Length THe length of Smm memory to be checked.
517 
518   @retval TRUE  Smm base~length is in smm memory map.
519   @retval FALSE Smm base~length is in smm memory map.
520 **/
521 BOOLEAN
SmmMemoryMapConsistencyCheckRange(IN EFI_PHYSICAL_ADDRESS Base,IN UINTN Length)522 SmmMemoryMapConsistencyCheckRange (
523   IN EFI_PHYSICAL_ADDRESS Base,
524   IN UINTN                Length
525   )
526 {
527   LIST_ENTRY               *Link;
528   MEMORY_MAP               *Entry;
529   BOOLEAN                  Result;
530 
531   Result = FALSE;
532   Link = gMemoryMap.ForwardLink;
533   while (Link != &gMemoryMap) {
534     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
535     Link  = Link->ForwardLink;
536 
537     if (Entry->Type != EfiConventionalMemory) {
538       continue;
539     }
540     if (Entry->Start == Base && Entry->End == Base + Length - 1) {
541       Result = TRUE;
542       break;
543     }
544   }
545 
546   return Result;
547 }
548 
549 /**
550   Check the consistency of Smm memory map.
551 **/
552 VOID
SmmMemoryMapConsistencyCheck(VOID)553 SmmMemoryMapConsistencyCheck (
554   VOID
555   )
556 {
557   LIST_ENTRY      *Node;
558   FREE_PAGE_LIST  *Pages;
559   BOOLEAN         Result;
560 
561   Pages = NULL;
562   Node = mSmmMemoryMap.ForwardLink;
563   while (Node != &mSmmMemoryMap) {
564     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
565     Result = SmmMemoryMapConsistencyCheckRange ((EFI_PHYSICAL_ADDRESS)(UINTN)Pages, (UINTN)EFI_PAGES_TO_SIZE(Pages->NumberOfPages));
566     ASSERT (Result);
567     Node = Node->ForwardLink;
568   }
569 }
570 
571 /**
572   Internal Function. Allocate n pages from given free page node.
573 
574   @param  Pages                  The free page node.
575   @param  NumberOfPages          Number of pages to be allocated.
576   @param  MaxAddress             Request to allocate memory below this address.
577 
578   @return Memory address of allocated pages.
579 
580 **/
581 UINTN
InternalAllocPagesOnOneNode(IN OUT FREE_PAGE_LIST * Pages,IN UINTN NumberOfPages,IN UINTN MaxAddress)582 InternalAllocPagesOnOneNode (
583   IN OUT FREE_PAGE_LIST  *Pages,
584   IN     UINTN           NumberOfPages,
585   IN     UINTN           MaxAddress
586   )
587 {
588   UINTN           Top;
589   UINTN           Bottom;
590   FREE_PAGE_LIST  *Node;
591 
592   Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);
593   if (Top > Pages->NumberOfPages) {
594     Top = Pages->NumberOfPages;
595   }
596   Bottom = Top - NumberOfPages;
597 
598   if (Top < Pages->NumberOfPages) {
599     Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));
600     Node->NumberOfPages = Pages->NumberOfPages - Top;
601     InsertHeadList (&Pages->Link, &Node->Link);
602   }
603 
604   if (Bottom > 0) {
605     Pages->NumberOfPages = Bottom;
606   } else {
607     RemoveEntryList (&Pages->Link);
608   }
609 
610   return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);
611 }
612 
613 /**
614   Internal Function. Allocate n pages from free page list below MaxAddress.
615 
616   @param  FreePageList           The free page node.
617   @param  NumberOfPages          Number of pages to be allocated.
618   @param  MaxAddress             Request to allocate memory below this address.
619 
620   @return Memory address of allocated pages.
621 
622 **/
623 UINTN
InternalAllocMaxAddress(IN OUT LIST_ENTRY * FreePageList,IN UINTN NumberOfPages,IN UINTN MaxAddress)624 InternalAllocMaxAddress (
625   IN OUT LIST_ENTRY  *FreePageList,
626   IN     UINTN       NumberOfPages,
627   IN     UINTN       MaxAddress
628   )
629 {
630   LIST_ENTRY      *Node;
631   FREE_PAGE_LIST  *Pages;
632 
633   for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {
634     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
635     if (Pages->NumberOfPages >= NumberOfPages &&
636         (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {
637       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);
638     }
639   }
640   return (UINTN)(-1);
641 }
642 
643 /**
644   Internal Function. Allocate n pages from free page list at given address.
645 
646   @param  FreePageList           The free page node.
647   @param  NumberOfPages          Number of pages to be allocated.
648   @param  MaxAddress             Request to allocate memory below this address.
649 
650   @return Memory address of allocated pages.
651 
652 **/
653 UINTN
InternalAllocAddress(IN OUT LIST_ENTRY * FreePageList,IN UINTN NumberOfPages,IN UINTN Address)654 InternalAllocAddress (
655   IN OUT LIST_ENTRY  *FreePageList,
656   IN     UINTN       NumberOfPages,
657   IN     UINTN       Address
658   )
659 {
660   UINTN           EndAddress;
661   LIST_ENTRY      *Node;
662   FREE_PAGE_LIST  *Pages;
663 
664   if ((Address & EFI_PAGE_MASK) != 0) {
665     return ~Address;
666   }
667 
668   EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);
669   for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {
670     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
671     if ((UINTN)Pages <= Address) {
672       if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {
673         break;
674       }
675       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);
676     }
677   }
678   return ~Address;
679 }
680 
681 /**
682   Allocates pages from the memory map.
683 
684   @param[in]   Type                   The type of allocation to perform.
685   @param[in]   MemoryType             The type of memory to turn the allocated pages
686                                       into.
687   @param[in]   NumberOfPages          The number of pages to allocate.
688   @param[out]  Memory                 A pointer to receive the base allocated memory
689                                       address.
690   @param[in]   AddRegion              If this memory is new added region.
691 
692   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
693   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
694   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
695   @retval EFI_SUCCESS            Pages successfully allocated.
696 
697 **/
698 EFI_STATUS
SmmInternalAllocatePagesEx(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory,IN BOOLEAN AddRegion)699 SmmInternalAllocatePagesEx (
700   IN  EFI_ALLOCATE_TYPE     Type,
701   IN  EFI_MEMORY_TYPE       MemoryType,
702   IN  UINTN                 NumberOfPages,
703   OUT EFI_PHYSICAL_ADDRESS  *Memory,
704   IN  BOOLEAN               AddRegion
705   )
706 {
707   UINTN  RequestedAddress;
708 
709   if (MemoryType != EfiRuntimeServicesCode &&
710       MemoryType != EfiRuntimeServicesData) {
711     return EFI_INVALID_PARAMETER;
712   }
713 
714   if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {
715     return EFI_OUT_OF_RESOURCES;
716   }
717 
718   //
719   // We don't track memory type in SMM
720   //
721   RequestedAddress = (UINTN)*Memory;
722   switch (Type) {
723     case AllocateAnyPages:
724       RequestedAddress = (UINTN)(-1);
725     case AllocateMaxAddress:
726       *Memory = InternalAllocMaxAddress (
727                   &mSmmMemoryMap,
728                   NumberOfPages,
729                   RequestedAddress
730                   );
731       if (*Memory == (UINTN)-1) {
732         return EFI_OUT_OF_RESOURCES;
733       }
734       break;
735     case AllocateAddress:
736       *Memory = InternalAllocAddress (
737                   &mSmmMemoryMap,
738                   NumberOfPages,
739                   RequestedAddress
740                   );
741       if (*Memory != RequestedAddress) {
742         return EFI_NOT_FOUND;
743       }
744       break;
745     default:
746       return EFI_INVALID_PARAMETER;
747   }
748 
749   //
750   // Update SmmMemoryMap here.
751   //
752   ConvertSmmMemoryMapEntry (MemoryType, *Memory, NumberOfPages, AddRegion);
753   if (!AddRegion) {
754     CoreFreeMemoryMapStack();
755   }
756 
757   return EFI_SUCCESS;
758 }
759 
760 /**
761   Allocates pages from the memory map.
762 
763   @param[in]   Type                   The type of allocation to perform.
764   @param[in]   MemoryType             The type of memory to turn the allocated pages
765                                       into.
766   @param[in]   NumberOfPages          The number of pages to allocate.
767   @param[out]  Memory                 A pointer to receive the base allocated memory
768                                       address.
769 
770   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
771   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
772   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
773   @retval EFI_SUCCESS            Pages successfully allocated.
774 
775 **/
776 EFI_STATUS
777 EFIAPI
SmmInternalAllocatePages(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory)778 SmmInternalAllocatePages (
779   IN  EFI_ALLOCATE_TYPE     Type,
780   IN  EFI_MEMORY_TYPE       MemoryType,
781   IN  UINTN                 NumberOfPages,
782   OUT EFI_PHYSICAL_ADDRESS  *Memory
783   )
784 {
785   return SmmInternalAllocatePagesEx (Type, MemoryType, NumberOfPages, Memory, FALSE);
786 }
787 
788 /**
789   Allocates pages from the memory map.
790 
791   @param  Type                   The type of allocation to perform.
792   @param  MemoryType             The type of memory to turn the allocated pages
793                                  into.
794   @param  NumberOfPages          The number of pages to allocate.
795   @param  Memory                 A pointer to receive the base allocated memory
796                                  address.
797 
798   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
799   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
800   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
801   @retval EFI_SUCCESS            Pages successfully allocated.
802 
803 **/
804 EFI_STATUS
805 EFIAPI
SmmAllocatePages(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory)806 SmmAllocatePages (
807   IN  EFI_ALLOCATE_TYPE     Type,
808   IN  EFI_MEMORY_TYPE       MemoryType,
809   IN  UINTN                 NumberOfPages,
810   OUT EFI_PHYSICAL_ADDRESS  *Memory
811   )
812 {
813   EFI_STATUS  Status;
814 
815   Status = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory);
816   if (!EFI_ERROR (Status)) {
817     SmmCoreUpdateProfile (
818       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
819       MemoryProfileActionAllocatePages,
820       MemoryType,
821       EFI_PAGES_TO_SIZE (NumberOfPages),
822       (VOID *) (UINTN) *Memory,
823       NULL
824       );
825   }
826   return Status;
827 }
828 
829 /**
830   Internal Function. Merge two adjacent nodes.
831 
832   @param  First             The first of two nodes to merge.
833 
834   @return Pointer to node after merge (if success) or pointer to next node (if fail).
835 
836 **/
837 FREE_PAGE_LIST *
InternalMergeNodes(IN FREE_PAGE_LIST * First)838 InternalMergeNodes (
839   IN FREE_PAGE_LIST  *First
840   )
841 {
842   FREE_PAGE_LIST  *Next;
843 
844   Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
845   ASSERT (
846     TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
847 
848   if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
849     First->NumberOfPages += Next->NumberOfPages;
850     RemoveEntryList (&Next->Link);
851     Next = First;
852   }
853   return Next;
854 }
855 
856 /**
857   Frees previous allocated pages.
858 
859   @param[in]  Memory                 Base address of memory being freed.
860   @param[in]  NumberOfPages          The number of pages to free.
861   @param[in]  AddRegion              If this memory is new added region.
862 
863   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
864   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
865   @return EFI_SUCCESS            Pages successfully freed.
866 
867 **/
868 EFI_STATUS
SmmInternalFreePagesEx(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages,IN BOOLEAN AddRegion)869 SmmInternalFreePagesEx (
870   IN EFI_PHYSICAL_ADDRESS  Memory,
871   IN UINTN                 NumberOfPages,
872   IN BOOLEAN               AddRegion
873   )
874 {
875   LIST_ENTRY      *Node;
876   FREE_PAGE_LIST  *Pages;
877 
878   if (((Memory & EFI_PAGE_MASK) != 0) || (Memory == 0) || (NumberOfPages == 0)) {
879     return EFI_INVALID_PARAMETER;
880   }
881 
882   Pages = NULL;
883   Node = mSmmMemoryMap.ForwardLink;
884   while (Node != &mSmmMemoryMap) {
885     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
886     if (Memory < (UINTN)Pages) {
887       break;
888     }
889     Node = Node->ForwardLink;
890   }
891 
892   if (Node != &mSmmMemoryMap &&
893       Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
894     return EFI_INVALID_PARAMETER;
895   }
896 
897   if (Node->BackLink != &mSmmMemoryMap) {
898     Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
899     if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
900       return EFI_INVALID_PARAMETER;
901     }
902   }
903 
904   Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
905   Pages->NumberOfPages = NumberOfPages;
906   InsertTailList (Node, &Pages->Link);
907 
908   if (Pages->Link.BackLink != &mSmmMemoryMap) {
909     Pages = InternalMergeNodes (
910               BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
911               );
912   }
913 
914   if (Node != &mSmmMemoryMap) {
915     InternalMergeNodes (Pages);
916   }
917 
918   //
919   // Update SmmMemoryMap here.
920   //
921   ConvertSmmMemoryMapEntry (EfiConventionalMemory, Memory, NumberOfPages, AddRegion);
922   if (!AddRegion) {
923     CoreFreeMemoryMapStack();
924   }
925 
926   return EFI_SUCCESS;
927 }
928 
929 /**
930   Frees previous allocated pages.
931 
932   @param[in]  Memory                 Base address of memory being freed.
933   @param[in]  NumberOfPages          The number of pages to free.
934 
935   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
936   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
937   @return EFI_SUCCESS            Pages successfully freed.
938 
939 **/
940 EFI_STATUS
941 EFIAPI
SmmInternalFreePages(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages)942 SmmInternalFreePages (
943   IN EFI_PHYSICAL_ADDRESS  Memory,
944   IN UINTN                 NumberOfPages
945   )
946 {
947   return SmmInternalFreePagesEx (Memory, NumberOfPages, FALSE);
948 }
949 
950 /**
951   Frees previous allocated pages.
952 
953   @param  Memory                 Base address of memory being freed.
954   @param  NumberOfPages          The number of pages to free.
955 
956   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
957   @retval EFI_INVALID_PARAMETER  Address not aligned, Address is zero or NumberOfPages is zero.
958   @return EFI_SUCCESS            Pages successfully freed.
959 
960 **/
961 EFI_STATUS
962 EFIAPI
SmmFreePages(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages)963 SmmFreePages (
964   IN EFI_PHYSICAL_ADDRESS  Memory,
965   IN UINTN                 NumberOfPages
966   )
967 {
968   EFI_STATUS  Status;
969 
970   Status = SmmInternalFreePages (Memory, NumberOfPages);
971   if (!EFI_ERROR (Status)) {
972     SmmCoreUpdateProfile (
973       (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
974       MemoryProfileActionFreePages,
975       EfiMaxMemoryType,
976       EFI_PAGES_TO_SIZE (NumberOfPages),
977       (VOID *) (UINTN) Memory,
978       NULL
979       );
980   }
981   return Status;
982 }
983 
984 /**
985   Add free SMRAM region for use by memory service.
986 
987   @param  MemBase                Base address of memory region.
988   @param  MemLength              Length of the memory region.
989   @param  Type                   Memory type.
990   @param  Attributes             Memory region state.
991 
992 **/
993 VOID
SmmAddMemoryRegion(IN EFI_PHYSICAL_ADDRESS MemBase,IN UINT64 MemLength,IN EFI_MEMORY_TYPE Type,IN UINT64 Attributes)994 SmmAddMemoryRegion (
995   IN  EFI_PHYSICAL_ADDRESS  MemBase,
996   IN  UINT64                MemLength,
997   IN  EFI_MEMORY_TYPE       Type,
998   IN  UINT64                Attributes
999   )
1000 {
1001   UINTN  AlignedMemBase;
1002 
1003   //
1004   // Add EfiRuntimeServicesData for memory regions that is already allocated, needs testing, or needs ECC initialization
1005   //
1006   if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
1007     Type = EfiRuntimeServicesData;
1008   } else {
1009     Type = EfiConventionalMemory;
1010   }
1011 
1012   DEBUG ((DEBUG_INFO, "SmmAddMemoryRegion\n"));
1013   DEBUG ((DEBUG_INFO, "  MemBase    - 0x%lx\n", MemBase));
1014   DEBUG ((DEBUG_INFO, "  MemLength  - 0x%lx\n", MemLength));
1015   DEBUG ((DEBUG_INFO, "  Type       - 0x%x\n", Type));
1016   DEBUG ((DEBUG_INFO, "  Attributes - 0x%lx\n", Attributes));
1017 
1018   //
1019   // Align range on an EFI_PAGE_SIZE boundary
1020   //
1021   AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
1022   MemLength -= AlignedMemBase - MemBase;
1023   if (Type == EfiConventionalMemory) {
1024     SmmInternalFreePagesEx (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE);
1025   } else {
1026     ConvertSmmMemoryMapEntry (EfiRuntimeServicesData, AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength), TRUE);
1027   }
1028 
1029   CoreFreeMemoryMapStack ();
1030 }
1031 
1032 /**
1033   This function returns a copy of the current memory map. The map is an array of
1034   memory descriptors, each of which describes a contiguous block of memory.
1035 
1036   @param[in, out]  MemoryMapSize          A pointer to the size, in bytes, of the
1037                                           MemoryMap buffer. On input, this is the size of
1038                                           the buffer allocated by the caller.  On output,
1039                                           it is the size of the buffer returned by the
1040                                           firmware  if the buffer was large enough, or the
1041                                           size of the buffer needed  to contain the map if
1042                                           the buffer was too small.
1043   @param[in, out]  MemoryMap              A pointer to the buffer in which firmware places
1044                                           the current memory map.
1045   @param[out]      MapKey                 A pointer to the location in which firmware
1046                                           returns the key for the current memory map.
1047   @param[out]      DescriptorSize         A pointer to the location in which firmware
1048                                           returns the size, in bytes, of an individual
1049                                           EFI_MEMORY_DESCRIPTOR.
1050   @param[out]      DescriptorVersion      A pointer to the location in which firmware
1051                                           returns the version number associated with the
1052                                           EFI_MEMORY_DESCRIPTOR.
1053 
1054   @retval EFI_SUCCESS            The memory map was returned in the MemoryMap
1055                                  buffer.
1056   @retval EFI_BUFFER_TOO_SMALL   The MemoryMap buffer was too small. The current
1057                                  buffer size needed to hold the memory map is
1058                                  returned in MemoryMapSize.
1059   @retval EFI_INVALID_PARAMETER  One of the parameters has an invalid value.
1060 
1061 **/
1062 EFI_STATUS
1063 EFIAPI
SmmCoreGetMemoryMap(IN OUT UINTN * MemoryMapSize,IN OUT EFI_MEMORY_DESCRIPTOR * MemoryMap,OUT UINTN * MapKey,OUT UINTN * DescriptorSize,OUT UINT32 * DescriptorVersion)1064 SmmCoreGetMemoryMap (
1065   IN OUT UINTN                  *MemoryMapSize,
1066   IN OUT EFI_MEMORY_DESCRIPTOR  *MemoryMap,
1067   OUT UINTN                     *MapKey,
1068   OUT UINTN                     *DescriptorSize,
1069   OUT UINT32                    *DescriptorVersion
1070   )
1071 {
1072   UINTN                    Count;
1073   LIST_ENTRY               *Link;
1074   MEMORY_MAP               *Entry;
1075   UINTN                    Size;
1076   UINTN                    BufferSize;
1077 
1078   Size = sizeof (EFI_MEMORY_DESCRIPTOR);
1079 
1080   //
1081   // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will
1082   // prevent people from having pointer math bugs in their code.
1083   // now you have to use *DescriptorSize to make things work.
1084   //
1085   Size += sizeof(UINT64) - (Size % sizeof (UINT64));
1086 
1087   if (DescriptorSize != NULL) {
1088     *DescriptorSize = Size;
1089   }
1090 
1091   if (DescriptorVersion != NULL) {
1092     *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION;
1093   }
1094 
1095   Count = GetSmmMemoryMapEntryCount ();
1096   BufferSize = Size * Count;
1097   if (*MemoryMapSize < BufferSize) {
1098     *MemoryMapSize = BufferSize;
1099     return EFI_BUFFER_TOO_SMALL;
1100   }
1101 
1102   *MemoryMapSize = BufferSize;
1103   if (MemoryMap == NULL) {
1104     return EFI_INVALID_PARAMETER;
1105   }
1106 
1107   ZeroMem (MemoryMap, BufferSize);
1108   Link = gMemoryMap.ForwardLink;
1109   while (Link != &gMemoryMap) {
1110     Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);
1111     Link  = Link->ForwardLink;
1112 
1113     MemoryMap->Type           = Entry->Type;
1114     MemoryMap->PhysicalStart  = Entry->Start;
1115     MemoryMap->NumberOfPages  = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);
1116 
1117     MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);
1118   }
1119 
1120   return EFI_SUCCESS;
1121 }
1122