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