]> git.proxmox.com Git - mirror_edk2.git/blob - StandaloneMmPkg/Core/Page.c
NetworkPkg: Move Network library header file from MdeModulePkg to NetworkPkg
[mirror_edk2.git] / StandaloneMmPkg / Core / Page.c
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
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
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
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
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
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 *
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
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
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
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 }