1 /** @file
2   MM Memory page management functions.
3 
4   Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.<BR>
5   Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.<BR>
6   SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include "StandaloneMmCore.h"
11 
12 #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \
13   ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size)))
14 
15 #define TRUNCATE_TO_PAGES(a)  ((a) >> EFI_PAGE_SHIFT)
16 
17 LIST_ENTRY  mMmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mMmMemoryMap);
18 
19 UINTN mMapKey;
20 
21 /**
22   Internal Function. Allocate n pages from given free page node.
23 
24   @param  Pages                  The free page node.
25   @param  NumberOfPages          Number of pages to be allocated.
26   @param  MaxAddress             Request to allocate memory below this address.
27 
28   @return Memory address of allocated pages.
29 
30 **/
31 UINTN
InternalAllocPagesOnOneNode(IN OUT FREE_PAGE_LIST * Pages,IN UINTN NumberOfPages,IN UINTN MaxAddress)32 InternalAllocPagesOnOneNode (
33   IN OUT FREE_PAGE_LIST  *Pages,
34   IN     UINTN           NumberOfPages,
35   IN     UINTN           MaxAddress
36   )
37 {
38   UINTN           Top;
39   UINTN           Bottom;
40   FREE_PAGE_LIST  *Node;
41 
42   Top = TRUNCATE_TO_PAGES (MaxAddress + 1 - (UINTN)Pages);
43   if (Top > Pages->NumberOfPages) {
44     Top = Pages->NumberOfPages;
45   }
46   Bottom = Top - NumberOfPages;
47 
48   if (Top < Pages->NumberOfPages) {
49     Node = (FREE_PAGE_LIST*)((UINTN)Pages + EFI_PAGES_TO_SIZE (Top));
50     Node->NumberOfPages = Pages->NumberOfPages - Top;
51     InsertHeadList (&Pages->Link, &Node->Link);
52   }
53 
54   if (Bottom > 0) {
55     Pages->NumberOfPages = Bottom;
56   } else {
57     RemoveEntryList (&Pages->Link);
58   }
59 
60   return (UINTN)Pages + EFI_PAGES_TO_SIZE (Bottom);
61 }
62 
63 /**
64   Internal Function. Allocate n pages from free page list below MaxAddress.
65 
66   @param  FreePageList           The free page node.
67   @param  NumberOfPages          Number of pages to be allocated.
68   @param  MaxAddress             Request to allocate memory below this address.
69 
70   @return Memory address of allocated pages.
71 
72 **/
73 UINTN
InternalAllocMaxAddress(IN OUT LIST_ENTRY * FreePageList,IN UINTN NumberOfPages,IN UINTN MaxAddress)74 InternalAllocMaxAddress (
75   IN OUT LIST_ENTRY  *FreePageList,
76   IN     UINTN       NumberOfPages,
77   IN     UINTN       MaxAddress
78   )
79 {
80   LIST_ENTRY      *Node;
81   FREE_PAGE_LIST  *Pages;
82 
83   for (Node = FreePageList->BackLink; Node != FreePageList; Node = Node->BackLink) {
84     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
85     if (Pages->NumberOfPages >= NumberOfPages &&
86         (UINTN)Pages + EFI_PAGES_TO_SIZE (NumberOfPages) - 1 <= MaxAddress) {
87       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, MaxAddress);
88     }
89   }
90   return (UINTN)(-1);
91 }
92 
93 /**
94   Internal Function. Allocate n pages from free page list at given address.
95 
96   @param  FreePageList           The free page node.
97   @param  NumberOfPages          Number of pages to be allocated.
98   @param  MaxAddress             Request to allocate memory below this address.
99 
100   @return Memory address of allocated pages.
101 
102 **/
103 UINTN
InternalAllocAddress(IN OUT LIST_ENTRY * FreePageList,IN UINTN NumberOfPages,IN UINTN Address)104 InternalAllocAddress (
105   IN OUT LIST_ENTRY  *FreePageList,
106   IN     UINTN       NumberOfPages,
107   IN     UINTN       Address
108   )
109 {
110   UINTN           EndAddress;
111   LIST_ENTRY      *Node;
112   FREE_PAGE_LIST  *Pages;
113 
114   if ((Address & EFI_PAGE_MASK) != 0) {
115     return ~Address;
116   }
117 
118   EndAddress = Address + EFI_PAGES_TO_SIZE (NumberOfPages);
119   for (Node = FreePageList->BackLink; Node!= FreePageList; Node = Node->BackLink) {
120     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
121     if ((UINTN)Pages <= Address) {
122       if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) < EndAddress) {
123         break;
124       }
125       return InternalAllocPagesOnOneNode (Pages, NumberOfPages, EndAddress);
126     }
127   }
128   return ~Address;
129 }
130 
131 /**
132   Allocates pages from the memory map.
133 
134   @param  Type                   The type of allocation to perform.
135   @param  MemoryType             The type of memory to turn the allocated pages
136                                  into.
137   @param  NumberOfPages          The number of pages to allocate.
138   @param  Memory                 A pointer to receive the base allocated memory
139                                  address.
140 
141   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
142   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
143   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
144   @retval EFI_SUCCESS            Pages successfully allocated.
145 
146 **/
147 EFI_STATUS
148 EFIAPI
MmInternalAllocatePages(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory)149 MmInternalAllocatePages (
150   IN  EFI_ALLOCATE_TYPE     Type,
151   IN  EFI_MEMORY_TYPE       MemoryType,
152   IN  UINTN                 NumberOfPages,
153   OUT EFI_PHYSICAL_ADDRESS  *Memory
154   )
155 {
156   UINTN  RequestedAddress;
157 
158   if (MemoryType != EfiRuntimeServicesCode &&
159       MemoryType != EfiRuntimeServicesData) {
160     return EFI_INVALID_PARAMETER;
161   }
162 
163   if (NumberOfPages > TRUNCATE_TO_PAGES ((UINTN)-1) + 1) {
164     return EFI_OUT_OF_RESOURCES;
165   }
166 
167   //
168   // We don't track memory type in MM
169   //
170   RequestedAddress = (UINTN)*Memory;
171   switch (Type) {
172     case AllocateAnyPages:
173       RequestedAddress = (UINTN)(-1);
174     case AllocateMaxAddress:
175       *Memory = InternalAllocMaxAddress (
176                   &mMmMemoryMap,
177                   NumberOfPages,
178                   RequestedAddress
179                   );
180       if (*Memory == (UINTN)-1) {
181         return EFI_OUT_OF_RESOURCES;
182       }
183       break;
184     case AllocateAddress:
185       *Memory = InternalAllocAddress (
186                   &mMmMemoryMap,
187                   NumberOfPages,
188                   RequestedAddress
189                   );
190       if (*Memory != RequestedAddress) {
191         return EFI_NOT_FOUND;
192       }
193       break;
194     default:
195       return EFI_INVALID_PARAMETER;
196   }
197   return EFI_SUCCESS;
198 }
199 
200 /**
201   Allocates pages from the memory map.
202 
203   @param  Type                   The type of allocation to perform.
204   @param  MemoryType             The type of memory to turn the allocated pages
205                                  into.
206   @param  NumberOfPages          The number of pages to allocate.
207   @param  Memory                 A pointer to receive the base allocated memory
208                                  address.
209 
210   @retval EFI_INVALID_PARAMETER  Parameters violate checking rules defined in spec.
211   @retval EFI_NOT_FOUND          Could not allocate pages match the requirement.
212   @retval EFI_OUT_OF_RESOURCES   No enough pages to allocate.
213   @retval EFI_SUCCESS            Pages successfully allocated.
214 
215 **/
216 EFI_STATUS
217 EFIAPI
MmAllocatePages(IN EFI_ALLOCATE_TYPE Type,IN EFI_MEMORY_TYPE MemoryType,IN UINTN NumberOfPages,OUT EFI_PHYSICAL_ADDRESS * Memory)218 MmAllocatePages (
219   IN  EFI_ALLOCATE_TYPE     Type,
220   IN  EFI_MEMORY_TYPE       MemoryType,
221   IN  UINTN                 NumberOfPages,
222   OUT EFI_PHYSICAL_ADDRESS  *Memory
223   )
224 {
225   EFI_STATUS  Status;
226 
227   Status = MmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory);
228   return Status;
229 }
230 
231 /**
232   Internal Function. Merge two adjacent nodes.
233 
234   @param  First             The first of two nodes to merge.
235 
236   @return Pointer to node after merge (if success) or pointer to next node (if fail).
237 
238 **/
239 FREE_PAGE_LIST *
InternalMergeNodes(IN FREE_PAGE_LIST * First)240 InternalMergeNodes (
241   IN FREE_PAGE_LIST  *First
242   )
243 {
244   FREE_PAGE_LIST  *Next;
245 
246   Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
247   ASSERT (
248     TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
249 
250   if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
251     First->NumberOfPages += Next->NumberOfPages;
252     RemoveEntryList (&Next->Link);
253     Next = First;
254   }
255   return Next;
256 }
257 
258 /**
259   Frees previous allocated pages.
260 
261   @param  Memory                 Base address of memory being freed.
262   @param  NumberOfPages          The number of pages to free.
263 
264   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
265   @retval EFI_INVALID_PARAMETER  Address not aligned.
266   @return EFI_SUCCESS            Pages successfully freed.
267 
268 **/
269 EFI_STATUS
270 EFIAPI
MmInternalFreePages(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages)271 MmInternalFreePages (
272   IN EFI_PHYSICAL_ADDRESS  Memory,
273   IN UINTN                 NumberOfPages
274   )
275 {
276   LIST_ENTRY      *Node;
277   FREE_PAGE_LIST  *Pages;
278 
279   if ((Memory & EFI_PAGE_MASK) != 0) {
280     return EFI_INVALID_PARAMETER;
281   }
282 
283   Pages = NULL;
284   Node = mMmMemoryMap.ForwardLink;
285   while (Node != &mMmMemoryMap) {
286     Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
287     if (Memory < (UINTN)Pages) {
288       break;
289     }
290     Node = Node->ForwardLink;
291   }
292 
293   if (Node != &mMmMemoryMap &&
294       Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
295     return EFI_INVALID_PARAMETER;
296   }
297 
298   if (Node->BackLink != &mMmMemoryMap) {
299     Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
300     if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
301       return EFI_INVALID_PARAMETER;
302     }
303   }
304 
305   Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
306   Pages->NumberOfPages = NumberOfPages;
307   InsertTailList (Node, &Pages->Link);
308 
309   if (Pages->Link.BackLink != &mMmMemoryMap) {
310     Pages = InternalMergeNodes (
311               BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
312               );
313   }
314 
315   if (Node != &mMmMemoryMap) {
316     InternalMergeNodes (Pages);
317   }
318 
319   return EFI_SUCCESS;
320 }
321 
322 /**
323   Frees previous allocated pages.
324 
325   @param  Memory                 Base address of memory being freed.
326   @param  NumberOfPages          The number of pages to free.
327 
328   @retval EFI_NOT_FOUND          Could not find the entry that covers the range.
329   @retval EFI_INVALID_PARAMETER  Address not aligned.
330   @return EFI_SUCCESS            Pages successfully freed.
331 
332 **/
333 EFI_STATUS
334 EFIAPI
MmFreePages(IN EFI_PHYSICAL_ADDRESS Memory,IN UINTN NumberOfPages)335 MmFreePages (
336   IN EFI_PHYSICAL_ADDRESS  Memory,
337   IN UINTN                 NumberOfPages
338   )
339 {
340   EFI_STATUS  Status;
341 
342   Status = MmInternalFreePages (Memory, NumberOfPages);
343   return Status;
344 }
345 
346 /**
347   Add free MMRAM region for use by memory service.
348 
349   @param  MemBase                Base address of memory region.
350   @param  MemLength              Length of the memory region.
351   @param  Type                   Memory type.
352   @param  Attributes             Memory region state.
353 
354 **/
355 VOID
MmAddMemoryRegion(IN EFI_PHYSICAL_ADDRESS MemBase,IN UINT64 MemLength,IN EFI_MEMORY_TYPE Type,IN UINT64 Attributes)356 MmAddMemoryRegion (
357   IN  EFI_PHYSICAL_ADDRESS  MemBase,
358   IN  UINT64                MemLength,
359   IN  EFI_MEMORY_TYPE       Type,
360   IN  UINT64                Attributes
361   )
362 {
363   UINTN  AlignedMemBase;
364 
365   //
366   // Do not add memory regions that is already allocated, needs testing, or needs ECC initialization
367   //
368   if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
369     return;
370   }
371 
372   //
373   // Align range on an EFI_PAGE_SIZE boundary
374   //
375   AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
376   MemLength -= AlignedMemBase - MemBase;
377   MmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength));
378 }
379