]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/PiSmmCore/Page.c
MdeModulePkg PiSmmCore: Enhance memory profile for memory leak detection
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Page.c
1 /** @file
2 SMM Memory page management functions.
3
4 Copyright (c) 2009 - 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials are licensed and made available
6 under the terms and conditions of the BSD License which accompanies this
7 distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "PiSmmCore.h"
16
17 #define TRUNCATE_TO_PAGES(a) ((a) >> EFI_PAGE_SHIFT)
18
19 LIST_ENTRY mSmmMemoryMap = INITIALIZE_LIST_HEAD_VARIABLE (mSmmMemoryMap);
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 SmmInternalAllocatePages (
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 SMM
169 //
170 RequestedAddress = (UINTN)*Memory;
171 switch (Type) {
172 case AllocateAnyPages:
173 RequestedAddress = (UINTN)(-1);
174 case AllocateMaxAddress:
175 *Memory = InternalAllocMaxAddress (
176 &mSmmMemoryMap,
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 &mSmmMemoryMap,
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 SmmAllocatePages (
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 = SmmInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory);
228 if (!EFI_ERROR (Status)) {
229 SmmCoreUpdateProfile (
230 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
231 MemoryProfileActionAllocatePages,
232 MemoryType,
233 EFI_PAGES_TO_SIZE (NumberOfPages),
234 (VOID *) (UINTN) *Memory,
235 NULL
236 );
237 }
238 return Status;
239 }
240
241 /**
242 Internal Function. Merge two adjacent nodes.
243
244 @param First The first of two nodes to merge.
245
246 @return Pointer to node after merge (if success) or pointer to next node (if fail).
247
248 **/
249 FREE_PAGE_LIST *
250 InternalMergeNodes (
251 IN FREE_PAGE_LIST *First
252 )
253 {
254 FREE_PAGE_LIST *Next;
255
256 Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
257 ASSERT (
258 TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
259
260 if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
261 First->NumberOfPages += Next->NumberOfPages;
262 RemoveEntryList (&Next->Link);
263 Next = First;
264 }
265 return Next;
266 }
267
268 /**
269 Frees previous allocated pages.
270
271 @param Memory Base address of memory being freed.
272 @param NumberOfPages The number of pages to free.
273
274 @retval EFI_NOT_FOUND Could not find the entry that covers the range.
275 @retval EFI_INVALID_PARAMETER Address not aligned.
276 @return EFI_SUCCESS Pages successfully freed.
277
278 **/
279 EFI_STATUS
280 EFIAPI
281 SmmInternalFreePages (
282 IN EFI_PHYSICAL_ADDRESS Memory,
283 IN UINTN NumberOfPages
284 )
285 {
286 LIST_ENTRY *Node;
287 FREE_PAGE_LIST *Pages;
288
289 if ((Memory & EFI_PAGE_MASK) != 0) {
290 return EFI_INVALID_PARAMETER;
291 }
292
293 Pages = NULL;
294 Node = mSmmMemoryMap.ForwardLink;
295 while (Node != &mSmmMemoryMap) {
296 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
297 if (Memory < (UINTN)Pages) {
298 break;
299 }
300 Node = Node->ForwardLink;
301 }
302
303 if (Node != &mSmmMemoryMap &&
304 Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
305 return EFI_INVALID_PARAMETER;
306 }
307
308 if (Node->BackLink != &mSmmMemoryMap) {
309 Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
310 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
311 return EFI_INVALID_PARAMETER;
312 }
313 }
314
315 Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
316 Pages->NumberOfPages = NumberOfPages;
317 InsertTailList (Node, &Pages->Link);
318
319 if (Pages->Link.BackLink != &mSmmMemoryMap) {
320 Pages = InternalMergeNodes (
321 BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
322 );
323 }
324
325 if (Node != &mSmmMemoryMap) {
326 InternalMergeNodes (Pages);
327 }
328
329 return EFI_SUCCESS;
330 }
331
332 /**
333 Frees previous allocated pages.
334
335 @param Memory Base address of memory being freed.
336 @param NumberOfPages The number of pages to free.
337
338 @retval EFI_NOT_FOUND Could not find the entry that covers the range.
339 @retval EFI_INVALID_PARAMETER Address not aligned.
340 @return EFI_SUCCESS Pages successfully freed.
341
342 **/
343 EFI_STATUS
344 EFIAPI
345 SmmFreePages (
346 IN EFI_PHYSICAL_ADDRESS Memory,
347 IN UINTN NumberOfPages
348 )
349 {
350 EFI_STATUS Status;
351
352 Status = SmmInternalFreePages (Memory, NumberOfPages);
353 if (!EFI_ERROR (Status)) {
354 SmmCoreUpdateProfile (
355 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),
356 MemoryProfileActionFreePages,
357 EfiMaxMemoryType,
358 EFI_PAGES_TO_SIZE (NumberOfPages),
359 (VOID *) (UINTN) Memory,
360 NULL
361 );
362 }
363 return Status;
364 }
365
366 /**
367 Add free SMRAM region for use by memory service.
368
369 @param MemBase Base address of memory region.
370 @param MemLength Length of the memory region.
371 @param Type Memory type.
372 @param Attributes Memory region state.
373
374 **/
375 VOID
376 SmmAddMemoryRegion (
377 IN EFI_PHYSICAL_ADDRESS MemBase,
378 IN UINT64 MemLength,
379 IN EFI_MEMORY_TYPE Type,
380 IN UINT64 Attributes
381 )
382 {
383 UINTN AlignedMemBase;
384
385 //
386 // Do not add memory regions that is already allocated, needs testing, or needs ECC initialization
387 //
388 if ((Attributes & (EFI_ALLOCATED | EFI_NEEDS_TESTING | EFI_NEEDS_ECC_INITIALIZATION)) != 0) {
389 return;
390 }
391
392 //
393 // Align range on an EFI_PAGE_SIZE boundary
394 //
395 AlignedMemBase = (UINTN)(MemBase + EFI_PAGE_MASK) & ~EFI_PAGE_MASK;
396 MemLength -= AlignedMemBase - MemBase;
397 SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength));
398 }