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