]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/PiSmmCore/Page.c
MdeModulePkg DxeCore/PiSmmCore: Add UEFI memory and SMRAM profile support.
[mirror_edk2.git] / MdeModulePkg / Core / PiSmmCore / Page.c
1 /** @file
2 SMM Memory page management functions.
3
4 Copyright (c) 2009 - 2014, 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 ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionAllocatePages, MemoryType, EFI_PAGES_TO_SIZE (NumberOfPages), (VOID *) (UINTN) *Memory);
230 }
231 return Status;
232 }
233
234 /**
235 Internal Function. Merge two adjacent nodes.
236
237 @param First The first of two nodes to merge.
238
239 @return Pointer to node after merge (if success) or pointer to next node (if fail).
240
241 **/
242 FREE_PAGE_LIST *
243 InternalMergeNodes (
244 IN FREE_PAGE_LIST *First
245 )
246 {
247 FREE_PAGE_LIST *Next;
248
249 Next = BASE_CR (First->Link.ForwardLink, FREE_PAGE_LIST, Link);
250 ASSERT (
251 TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) >= First->NumberOfPages);
252
253 if (TRUNCATE_TO_PAGES ((UINTN)Next - (UINTN)First) == First->NumberOfPages) {
254 First->NumberOfPages += Next->NumberOfPages;
255 RemoveEntryList (&Next->Link);
256 Next = First;
257 }
258 return Next;
259 }
260
261 /**
262 Frees previous allocated pages.
263
264 @param Memory Base address of memory being freed.
265 @param NumberOfPages The number of pages to free.
266
267 @retval EFI_NOT_FOUND Could not find the entry that covers the range.
268 @retval EFI_INVALID_PARAMETER Address not aligned.
269 @return EFI_SUCCESS Pages successfully freed.
270
271 **/
272 EFI_STATUS
273 EFIAPI
274 SmmInternalFreePages (
275 IN EFI_PHYSICAL_ADDRESS Memory,
276 IN UINTN NumberOfPages
277 )
278 {
279 LIST_ENTRY *Node;
280 FREE_PAGE_LIST *Pages;
281
282 if ((Memory & EFI_PAGE_MASK) != 0) {
283 return EFI_INVALID_PARAMETER;
284 }
285
286 Pages = NULL;
287 Node = mSmmMemoryMap.ForwardLink;
288 while (Node != &mSmmMemoryMap) {
289 Pages = BASE_CR (Node, FREE_PAGE_LIST, Link);
290 if (Memory < (UINTN)Pages) {
291 break;
292 }
293 Node = Node->ForwardLink;
294 }
295
296 if (Node != &mSmmMemoryMap &&
297 Memory + EFI_PAGES_TO_SIZE (NumberOfPages) > (UINTN)Pages) {
298 return EFI_INVALID_PARAMETER;
299 }
300
301 if (Node->BackLink != &mSmmMemoryMap) {
302 Pages = BASE_CR (Node->BackLink, FREE_PAGE_LIST, Link);
303 if ((UINTN)Pages + EFI_PAGES_TO_SIZE (Pages->NumberOfPages) > Memory) {
304 return EFI_INVALID_PARAMETER;
305 }
306 }
307
308 Pages = (FREE_PAGE_LIST*)(UINTN)Memory;
309 Pages->NumberOfPages = NumberOfPages;
310 InsertTailList (Node, &Pages->Link);
311
312 if (Pages->Link.BackLink != &mSmmMemoryMap) {
313 Pages = InternalMergeNodes (
314 BASE_CR (Pages->Link.BackLink, FREE_PAGE_LIST, Link)
315 );
316 }
317
318 if (Node != &mSmmMemoryMap) {
319 InternalMergeNodes (Pages);
320 }
321
322 return EFI_SUCCESS;
323 }
324
325 /**
326 Frees previous allocated pages.
327
328 @param Memory Base address of memory being freed.
329 @param NumberOfPages The number of pages to free.
330
331 @retval EFI_NOT_FOUND Could not find the entry that covers the range.
332 @retval EFI_INVALID_PARAMETER Address not aligned.
333 @return EFI_SUCCESS Pages successfully freed.
334
335 **/
336 EFI_STATUS
337 EFIAPI
338 SmmFreePages (
339 IN EFI_PHYSICAL_ADDRESS Memory,
340 IN UINTN NumberOfPages
341 )
342 {
343 EFI_STATUS Status;
344
345 Status = SmmInternalFreePages (Memory, NumberOfPages);
346 if (!EFI_ERROR (Status)) {
347 SmmCoreUpdateProfile ((EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0), MemoryProfileActionFreePages, 0, EFI_PAGES_TO_SIZE (NumberOfPages), (VOID *) (UINTN) Memory);
348 }
349 return Status;
350 }
351
352 /**
353 Add free SMRAM 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 SmmAddMemoryRegion (
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 SmmFreePages (AlignedMemBase, TRUNCATE_TO_PAGES ((UINTN)MemLength));
384 }