]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Core/PiSmmCore/Page.c
MdeModulePkg/dec: Add gEdkiiPiSmmMemoryAttributesTableGuid.
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Page.c
CommitLineData
e42e9404 1/** @file\r
2 SMM Memory page management functions.\r
3\r
e524f680 4 Copyright (c) 2009 - 2016, 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
e524f680
SZ
229 SmmCoreUpdateProfile (\r
230 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),\r
231 MemoryProfileActionAllocatePages,\r
232 MemoryType,\r
233 EFI_PAGES_TO_SIZE (NumberOfPages),\r
234 (VOID *) (UINTN) *Memory,\r
235 NULL\r
236 );\r
84edd20b
SZ
237 }\r
238 return Status;\r
239}\r
240\r
e42e9404 241/**\r
242 Internal Function. Merge two adjacent nodes.\r
243\r
244 @param First The first of two nodes to merge.\r
245\r
246 @return Pointer to node after merge (if success) or pointer to next node (if fail).\r
247\r
248**/\r
249FREE_PAGE_LIST *\r
250InternalMergeNodes (\r
251 IN FREE_PAGE_LIST *First\r
252 )\r
253{\r
254 FREE_PAGE_LIST *Next;\r
255\r
256 Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);\r
257 ASSERT (\r
258 TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);\r
259\r
260 if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {\r
261 First->NumberOfPages += Next->NumberOfPages;\r
262 RemoveEntryList (&Next->Link);\r
263 Next = First;\r
264 }\r
265 return Next;\r
266}\r
267\r
268/**\r
269 Frees previous allocated pages.\r
270\r
271 @param Memory Base address of memory being freed.\r
272 @param NumberOfPages The number of pages to free.\r
273\r
274 @retval EFI_NOT_FOUND Could not find the entry that covers the range.\r
275 @retval EFI_INVALID_PARAMETER Address not aligned.\r
276 @return EFI_SUCCESS Pages successfully freed.\r
277\r
278**/\r
279EFI_STATUS\r
280EFIAPI\r
84edd20b 281SmmInternalFreePages (\r
e42e9404 282 IN EFI_PHYSICAL_ADDRESS Memory,\r
283 IN UINTN NumberOfPages\r
284 )\r
285{\r
286 LIST_ENTRY *Node;\r
287 FREE_PAGE_LIST *Pages;\r
288\r
289 if ((Memory & EFI_PAGE_MASK) != 0) {\r
290 return EFI_INVALID_PARAMETER;\r
291 }\r
292\r
293 Pages = NULL;\r
294 Node = mSmmMemoryMap.ForwardLink;\r
295 while (Node != &mSmmMemoryMap) {\r
296 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);\r
297 if (Memory < (UINTN)Pages) {\r
298 break;\r
299 }\r
300 Node = Node->ForwardLink;\r
301 }\r
302\r
303 if (Node != &mSmmMemoryMap &&\r
304 Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {\r
305 return EFI_INVALID_PARAMETER;\r
306 }\r
307\r
308 if (Node->BackLink != &mSmmMemoryMap) {\r
309 Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);\r
310 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {\r
311 return EFI_INVALID_PARAMETER;\r
312 }\r
313 }\r
314\r
315 Pages = (FREE_PAGE_LIST*)(UINTN)Memory;\r
316 Pages->NumberOfPages = NumberOfPages;\r
317 InsertTailList (Node, &Pages->Link);\r
318\r
319 if (Pages->Link.BackLink != &mSmmMemoryMap) {\r
320 Pages = InternalMergeNodes (\r
321 BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)\r
322 );\r
323 }\r
324\r
325 if (Node != &mSmmMemoryMap) {\r
326 InternalMergeNodes (Pages);\r
327 }\r
328\r
329 return EFI_SUCCESS;\r
330}\r
331\r
84edd20b
SZ
332/**\r
333 Frees previous allocated pages.\r
334\r
335 @param Memory Base address of memory being freed.\r
336 @param NumberOfPages The number of pages to free.\r
337\r
338 @retval EFI_NOT_FOUND Could not find the entry that covers the range.\r
339 @retval EFI_INVALID_PARAMETER Address not aligned.\r
340 @return EFI_SUCCESS Pages successfully freed.\r
341\r
342**/\r
343EFI_STATUS\r
344EFIAPI\r
345SmmFreePages (\r
346 IN EFI_PHYSICAL_ADDRESS Memory,\r
347 IN UINTN NumberOfPages\r
348 )\r
349{\r
350 EFI_STATUS Status;\r
351\r
352 Status = SmmInternalFreePages (Memory, NumberOfPages);\r
353 if (!EFI_ERROR (Status)) {\r
e524f680
SZ
354 SmmCoreUpdateProfile (\r
355 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),\r
356 MemoryProfileActionFreePages,\r
357 EfiMaxMemoryType,\r
358 EFI_PAGES_TO_SIZE (NumberOfPages),\r
359 (VOID *) (UINTN) Memory,\r
360 NULL\r
361 );\r
84edd20b
SZ
362 }\r
363 return Status;\r
364}\r
365\r
e42e9404 366/**\r
367 Add free SMRAM region for use by memory service.\r
368\r
369 @param MemBase Base address of memory region.\r
370 @param MemLength Length of the memory region.\r
371 @param Type Memory type.\r
372 @param Attributes Memory region state.\r
373\r
374**/\r
375VOID\r
376SmmAddMemoryRegion (\r
377 IN EFI_PHYSICAL_ADDRESS MemBase,\r
378 IN UINT64 MemLength,\r
379 IN EFI_MEMORY_TYPE Type,\r
380 IN UINT64 Attributes\r
381 )\r
382{\r
383 UINTN AlignedMemBase;\r
384\r
3df4b6e7 385 //\r
386 // Do not add memory regions that is already allocated, needs testing, or needs ECC initialization\r
387 //\r
388 if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {\r
389 return;\r
390 }\r
391 \r
392 //\r
393 // Align range on an EFI_PAGE_SIZE boundary\r
394 // \r
e42e9404 395 AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;\r
396 MemLength -= AlignedMemBase - MemBase;\r
397 SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength));\r
398}\r