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