]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Core/Dxe/Mem/Page.c
Code scrub for DiskIo, Partition & Unicode Collation
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Mem / Page.c
... / ...
CommitLineData
1/** @file\r
2 UEFI Memory page management functions.\r
3\r
4Copyright (c) 2007 - 2008, Intel Corporation. <BR>\r
5All rights reserved. This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include <DxeMain.h>\r
16\r
17#define EFI_DEFAULT_PAGE_ALLOCATION_ALIGNMENT (EFI_PAGE_SIZE)\r
18\r
19//\r
20// Entry for tracking the memory regions for each memory type to help cooalese like memory types\r
21//\r
22typedef struct {\r
23 EFI_PHYSICAL_ADDRESS BaseAddress;\r
24 EFI_PHYSICAL_ADDRESS MaximumAddress;\r
25 UINT64 CurrentNumberOfPages;\r
26 UINT64 NumberOfPages;\r
27 UINTN InformationIndex;\r
28 BOOLEAN Special;\r
29 BOOLEAN Runtime;\r
30} EFI_MEMORY_TYPE_STAISTICS;\r
31\r
32//\r
33// MemoryMap - The current memory map\r
34//\r
35UINTN mMemoryMapKey = 0;\r
36\r
37//\r
38// mMapStack - space to use as temp storage to build new map descriptors\r
39// mMapDepth - depth of new descriptor stack\r
40//\r
41\r
42#define MAX_MAP_DEPTH 6\r
43UINTN mMapDepth = 0;\r
44MEMORY_MAP mMapStack[MAX_MAP_DEPTH];\r
45UINTN mFreeMapStack = 0;\r
46//\r
47// This list maintain the free memory map list\r
48//\r
49LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList);\r
50BOOLEAN mMemoryTypeInformationInitialized = FALSE;\r
51\r
52EFI_MEMORY_TYPE_STAISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = {\r
53 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiReservedMemoryType\r
54 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderCode\r
55 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderData\r
56 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesCode\r
57 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesData\r
58 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesCode\r
59 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesData\r
60 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiConventionalMemory\r
61 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiUnusableMemory\r
62 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIReclaimMemory\r
63 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIMemoryNVS\r
64 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIO\r
65 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIOPortSpace\r
66 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiPalCode\r
67 { 0, EFI_MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE } // EfiMaxMemoryType\r
68};\r
69\r
70EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress = EFI_MAX_ADDRESS;\r
71\r
72EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1] = {\r
73 { EfiReservedMemoryType, 0 },\r
74 { EfiLoaderCode, 0 },\r
75 { EfiLoaderData, 0 },\r
76 { EfiBootServicesCode, 0 },\r
77 { EfiBootServicesData, 0 },\r
78 { EfiRuntimeServicesCode, 0 },\r
79 { EfiRuntimeServicesData, 0 },\r
80 { EfiConventionalMemory, 0 },\r
81 { EfiUnusableMemory, 0 },\r
82 { EfiACPIReclaimMemory, 0 },\r
83 { EfiACPIMemoryNVS, 0 },\r
84 { EfiMemoryMappedIO, 0 },\r
85 { EfiMemoryMappedIOPortSpace, 0 },\r
86 { EfiPalCode, 0 },\r
87 { EfiMaxMemoryType, 0 }\r
88};\r
89\r
90//\r
91// Internal prototypes\r
92//\r
93/**\r
94 Find untested but initialized memory regions in GCD map and convert them to be DXE allocatable.\r
95\r
96**/\r
97VOID\r
98PromoteMemoryResource (\r
99 VOID\r
100 );\r
101\r
102/**\r
103 Internal function. Adds a ranges to the memory map.\r
104 The range must not already exist in the map.\r
105\r
106 @param Type The type of memory range to add \r
107 @param Start The starting address in the memory range Must be \r
108 paged aligned \r
109 @param End The last address in the range Must be the last \r
110 byte of a page \r
111 @param Attribute The attributes of the memory range to add \r
112\r
113 @return None. The range is added to the memory map\r
114\r
115**/\r
116VOID\r
117CoreAddRange (\r
118 IN EFI_MEMORY_TYPE Type,\r
119 IN EFI_PHYSICAL_ADDRESS Start,\r
120 IN EFI_PHYSICAL_ADDRESS End,\r
121 IN UINT64 Attribute\r
122 );\r
123\r
124/**\r
125 Internal function. Moves any memory descriptors that are on the\r
126 temporary descriptor stack to heap.\r
127\r
128**/\r
129VOID\r
130CoreFreeMemoryMapStack (\r
131 VOID\r
132 );\r
133\r
134/**\r
135 Internal function. Converts a memory range to the specified type.\r
136 The range must exist in the memory map.\r
137\r
138 @param Start The first address of the range Must be page \r
139 aligned \r
140 @param NumberOfPages The number of pages to convert \r
141 @param NewType The new type for the memory range \r
142\r
143 @retval EFI_INVALID_PARAMETER Invalid parameter \r
144 @retval EFI_NOT_FOUND Could not find a descriptor cover the specified \r
145 range or convertion not allowed. \r
146 @retval EFI_SUCCESS Successfully converts the memory range to the \r
147 specified type.\r
148\r
149**/\r
150EFI_STATUS\r
151CoreConvertPages (\r
152 IN UINT64 Start,\r
153 IN UINT64 NumberOfPages,\r
154 IN EFI_MEMORY_TYPE NewType\r
155 );\r
156\r
157/**\r
158 Internal function. Removes a descriptor entry.\r
159\r
160 @param Entry The entry to remove\r
161\r
162**/\r
163VOID\r
164RemoveMemoryMapEntry (\r
165 MEMORY_MAP *Entry\r
166 );\r
167 \r
168/**\r
169 Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList.\r
170 If the list is emtry, then allocate a new page to refuel the list.\r
171 Please Note this algorithm to allocate the memory map descriptor has a property\r
172 that the memory allocated for memory entries always grows, and will never really be freed\r
173 For example, if the current boot uses 2000 memory map entries at the maximum point, but\r
174 ends up with only 50 at the time the OS is booted, then the memory associated with the 1950\r
175 memory map entries is still allocated from EfiBootServicesMemory.\r
176\r
177\r
178 @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList\r
179\r
180**/\r
181MEMORY_MAP *\r
182AllocateMemoryMapEntry (\r
183 VOID\r
184 );\r
185 \r
186\r
187/**\r
188 Enter critical section by gaining lock on gMemoryLock.\r
189\r
190**/\r
191VOID\r
192CoreAcquireMemoryLock (\r
193 VOID\r
194 )\r
195{\r
196 CoreAcquireLock (&gMemoryLock);\r
197}\r
198\r
199\r
200\r
201/**\r
202 Exit critical section by releasing lock on gMemoryLock.\r
203\r
204**/\r
205VOID\r
206CoreReleaseMemoryLock (\r
207 VOID\r
208 )\r
209{\r
210 CoreReleaseLock (&gMemoryLock);\r
211}\r
212\r
213\r
214/**\r
215 Find untested but initialized memory regions in GCD map and convert them to be DXE allocatable.\r
216\r
217**/\r
218VOID\r
219PromoteMemoryResource (\r
220 VOID\r
221 )\r
222{\r
223 LIST_ENTRY *Link;\r
224 EFI_GCD_MAP_ENTRY *Entry;\r
225\r
226 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "Promote the memory resource\n"));\r
227 \r
228 CoreAcquireGcdMemoryLock ();\r
229 \r
230 Link = mGcdMemorySpaceMap.ForwardLink;\r
231 while (Link != &mGcdMemorySpaceMap) {\r
232\r
233 Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);\r
234\r
235 if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&\r
236 Entry->EndAddress < EFI_MAX_ADDRESS &&\r
237 (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==\r
238 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {\r
239 //\r
240 // Update the GCD map\r
241 //\r
242 Entry->GcdMemoryType = EfiGcdMemoryTypeSystemMemory;\r
243 Entry->Capabilities |= EFI_MEMORY_TESTED;\r
244 Entry->ImageHandle = gDxeCoreImageHandle;\r
245 Entry->DeviceHandle = NULL;\r
246\r
247 //\r
248 // Add to allocable system memory resource\r
249 // \r
250\r
251 CoreAddRange (\r
252 EfiConventionalMemory, \r
253 Entry->BaseAddress, \r
254 Entry->EndAddress, \r
255 Entry->Capabilities & ~(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME)\r
256 );\r
257 CoreFreeMemoryMapStack ();\r
258 \r
259 }\r
260\r
261 Link = Link->ForwardLink;\r
262 }\r
263 \r
264 CoreReleaseGcdMemoryLock ();\r
265 \r
266 return;\r
267}\r
268\r
269\r
270/**\r
271 Called to initialize the memory map and add descriptors to\r
272 the current descriptor list.\r
273 The first descriptor that is added must be general usable\r
274 memory as the addition allocates heap.\r
275\r
276 @param Type The type of memory to add \r
277 @param Start The starting address in the memory range Must be \r
278 page aligned \r
279 @param NumberOfPages The number of pages in the range \r
280 @param Attribute Attributes of the memory to add \r
281\r
282 @return None. The range is added to the memory map\r
283\r
284**/\r
285VOID\r
286CoreAddMemoryDescriptor (\r
287 IN EFI_MEMORY_TYPE Type,\r
288 IN EFI_PHYSICAL_ADDRESS Start,\r
289 IN UINT64 NumberOfPages,\r
290 IN UINT64 Attribute\r
291 )\r
292{\r
293 EFI_PHYSICAL_ADDRESS End;\r
294 EFI_STATUS Status;\r
295 UINTN Index;\r
296 UINTN FreeIndex;\r
297\r
298 if ((Start & EFI_PAGE_MASK) != 0) {\r
299 return;\r
300 }\r
301\r
302 if (Type >= EfiMaxMemoryType && Type <= 0x7fffffff) {\r
303 return;\r
304 }\r
305 \r
306 CoreAcquireMemoryLock ();\r
307 End = Start + LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) - 1;\r
308 CoreAddRange (Type, Start, End, Attribute);\r
309 CoreFreeMemoryMapStack ();\r
310 CoreReleaseMemoryLock ();\r
311\r
312 //\r
313 // Check to see if the statistics for the different memory types have already been established\r
314 //\r
315 if (mMemoryTypeInformationInitialized) {\r
316 return;\r
317 }\r
318\r
319 //\r
320 // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array\r
321 //\r
322 for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {\r
323 //\r
324 // Make sure the memory type in the gMemoryTypeInformation[] array is valid\r
325 //\r
326 Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[Index].Type);\r
327 if (Type < 0 || Type > EfiMaxMemoryType) {\r
328 continue;\r
329 }\r
330\r
331 if (gMemoryTypeInformation[Index].NumberOfPages != 0) {\r
332 //\r
333 // Allocate pages for the current memory type from the top of available memory\r
334 //\r
335 Status = CoreAllocatePages (\r
336 AllocateAnyPages,\r
337 Type,\r
338 gMemoryTypeInformation[Index].NumberOfPages,\r
339 &mMemoryTypeStatistics[Type].BaseAddress\r
340 );\r
341 if (EFI_ERROR (Status)) {\r
342 //\r
343 // If an error occurs allocating the pages for the current memory type, then \r
344 // free all the pages allocates for the previous memory types and return. This\r
345 // operation with be retied when/if more memory is added to the system\r
346 //\r
347 for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) {\r
348 //\r
349 // Make sure the memory type in the gMemoryTypeInformation[] array is valid\r
350 //\r
351 Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[FreeIndex].Type);\r
352 if (Type < 0 || Type > EfiMaxMemoryType) {\r
353 continue;\r
354 }\r
355\r
356 if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) {\r
357 CoreFreePages (\r
358 mMemoryTypeStatistics[Type].BaseAddress, \r
359 gMemoryTypeInformation[FreeIndex].NumberOfPages\r
360 );\r
361 mMemoryTypeStatistics[Type].BaseAddress = 0;\r
362 mMemoryTypeStatistics[Type].MaximumAddress = EFI_MAX_ADDRESS;\r
363 }\r
364 }\r
365 return;\r
366 }\r
367\r
368 //\r
369 // Compute the address at the top of the current statistics\r
370 //\r
371 mMemoryTypeStatistics[Type].MaximumAddress = \r
372 mMemoryTypeStatistics[Type].BaseAddress + \r
373 LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1;\r
374\r
375 //\r
376 // If the current base address is the lowest address so far, then update the default \r
377 // maximum address\r
378 //\r
379 if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) {\r
380 mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1;\r
381 }\r
382 }\r
383 }\r
384\r
385 //\r
386 // There was enough system memory for all the the memory types were allocated. So,\r
387 // those memory areas can be freed for future allocations, and all future memory\r
388 // allocations can occur within their respective bins\r
389 //\r
390 for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {\r
391 //\r
392 // Make sure the memory type in the gMemoryTypeInformation[] array is valid\r
393 //\r
394 Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[Index].Type);\r
395 if (Type < 0 || Type > EfiMaxMemoryType) {\r
396 continue;\r
397 }\r
398\r
399 if (gMemoryTypeInformation[Index].NumberOfPages != 0) {\r
400 CoreFreePages (\r
401 mMemoryTypeStatistics[Type].BaseAddress, \r
402 gMemoryTypeInformation[Index].NumberOfPages\r
403 );\r
404 mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages;\r
405 gMemoryTypeInformation[Index].NumberOfPages = 0;\r
406 }\r
407 }\r
408\r
409 //\r
410 // If the number of pages reserved for a memory type is 0, then all allocations for that type\r
411 // should be in the default range.\r
412 //\r
413 for (Type = (EFI_MEMORY_TYPE) 0; Type < EfiMaxMemoryType; Type++) {\r
414 for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {\r
415 if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) {\r
416 mMemoryTypeStatistics[Type].InformationIndex = Index;\r
417 }\r
418 }\r
419 mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0;\r
420 if (mMemoryTypeStatistics[Type].MaximumAddress == EFI_MAX_ADDRESS) {\r
421 mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress;\r
422 }\r
423 }\r
424\r
425 mMemoryTypeInformationInitialized = TRUE;\r
426}\r
427\r
428\r
429\r
430/**\r
431 Internal function. Adds a ranges to the memory map.\r
432 The range must not already exist in the map.\r
433\r
434 @param Type The type of memory range to add \r
435 @param Start The starting address in the memory range Must be \r
436 paged aligned \r
437 @param End The last address in the range Must be the last \r
438 byte of a page \r
439 @param Attribute The attributes of the memory range to add \r
440\r
441 @return None. The range is added to the memory map\r
442\r
443**/\r
444VOID\r
445CoreAddRange (\r
446 IN EFI_MEMORY_TYPE Type,\r
447 IN EFI_PHYSICAL_ADDRESS Start,\r
448 IN EFI_PHYSICAL_ADDRESS End,\r
449 IN UINT64 Attribute\r
450 )\r
451{\r
452 LIST_ENTRY *Link;\r
453 MEMORY_MAP *Entry;\r
454\r
455 ASSERT ((Start & EFI_PAGE_MASK) == 0);\r
456 ASSERT (End > Start) ;\r
457\r
458 ASSERT_LOCKED (&gMemoryLock);\r
459 \r
460 DEBUG ((DEBUG_PAGE, "AddRange: %lx-%lx to %d\n", Start, End, Type));\r
461\r
462 //\r
463 // Memory map being altered so updated key\r
464 //\r
465 mMemoryMapKey += 1;\r
466\r
467 //\r
468 // UEFI 2.0 added an event group for notificaiton on memory map changes.\r
469 // So we need to signal this Event Group every time the memory map changes.\r
470 // If we are in EFI 1.10 compatability mode no event groups will be \r
471 // found and nothing will happen we we call this function. These events\r
472 // will get signaled but since a lock is held around the call to this \r
473 // function the notificaiton events will only be called after this funciton\r
474 // returns and the lock is released.\r
475 //\r
476 CoreNotifySignalList (&gEfiEventMemoryMapChangeGuid);\r
477\r
478 //\r
479 // Look for adjoining memory descriptor\r
480 //\r
481 \r
482 // Two memory descriptors can only be merged if they have the same Type\r
483 // and the same Attribute\r
484 //\r
485\r
486 Link = gMemoryMap.ForwardLink;\r
487 while (Link != &gMemoryMap) {\r
488 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
489 Link = Link->ForwardLink;\r
490\r
491 if (Entry->Type != Type) {\r
492 continue;\r
493 }\r
494\r
495 if (Entry->Attribute != Attribute) {\r
496 continue;\r
497 }\r
498\r
499 if (Entry->End + 1 == Start) {\r
500 \r
501 Start = Entry->Start;\r
502 RemoveMemoryMapEntry (Entry);\r
503\r
504 } else if (Entry->Start == End + 1) {\r
505 \r
506 End = Entry->End;\r
507 RemoveMemoryMapEntry (Entry);\r
508 }\r
509 }\r
510\r
511 //\r
512 // Add descriptor \r
513 //\r
514\r
515 mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE;\r
516 mMapStack[mMapDepth].FromPages = FALSE;\r
517 mMapStack[mMapDepth].Type = Type;\r
518 mMapStack[mMapDepth].Start = Start;\r
519 mMapStack[mMapDepth].End = End;\r
520 mMapStack[mMapDepth].VirtualStart = 0;\r
521 mMapStack[mMapDepth].Attribute = Attribute;\r
522 InsertTailList (&gMemoryMap, &mMapStack[mMapDepth].Link);\r
523\r
524 mMapDepth += 1;\r
525 ASSERT (mMapDepth < MAX_MAP_DEPTH);\r
526\r
527 return ;\r
528}\r
529\r
530\r
531/**\r
532 Internal function. Moves any memory descriptors that are on the\r
533 temporary descriptor stack to heap.\r
534\r
535**/\r
536VOID\r
537CoreFreeMemoryMapStack (\r
538 VOID\r
539 )\r
540{\r
541 MEMORY_MAP *Entry;\r
542 MEMORY_MAP *Entry2;\r
543 LIST_ENTRY *Link2;\r
544\r
545 ASSERT_LOCKED (&gMemoryLock);\r
546\r
547 //\r
548 // If already freeing the map stack, then return\r
549 //\r
550 if (mFreeMapStack != 0) {\r
551 return ;\r
552 }\r
553\r
554 //\r
555 // Move the temporary memory descriptor stack into pool\r
556 //\r
557 mFreeMapStack += 1;\r
558\r
559 while (mMapDepth != 0) {\r
560 //\r
561 // Deque an memory map entry from mFreeMemoryMapEntryList \r
562 //\r
563 Entry = AllocateMemoryMapEntry ();\r
564 \r
565 ASSERT (Entry);\r
566\r
567 //\r
568 // Update to proper entry\r
569 //\r
570 mMapDepth -= 1;\r
571\r
572 if (mMapStack[mMapDepth].Link.ForwardLink != NULL) {\r
573\r
574 //\r
575 // Move this entry to general memory\r
576 //\r
577 RemoveEntryList (&mMapStack[mMapDepth].Link);\r
578 mMapStack[mMapDepth].Link.ForwardLink = NULL;\r
579\r
580 CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP));\r
581 Entry->FromPages = TRUE;\r
582\r
583 //\r
584 // Find insertion location\r
585 //\r
586 for (Link2 = gMemoryMap.ForwardLink; Link2 != &gMemoryMap; Link2 = Link2->ForwardLink) {\r
587 Entry2 = CR (Link2, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
588 if (Entry2->FromPages && Entry2->Start > Entry->Start) {\r
589 break;\r
590 }\r
591 }\r
592\r
593 InsertTailList (Link2, &Entry->Link);\r
594\r
595 } else {\r
596 // \r
597 // This item of mMapStack[mMapDepth] has already been dequeued from gMemoryMap list,\r
598 // so here no need to move it to memory.\r
599 //\r
600 InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);\r
601 }\r
602 }\r
603\r
604 mFreeMapStack -= 1;\r
605}\r
606\r
607\r
608/**\r
609 Internal function. Removes a descriptor entry.\r
610\r
611 @param Entry The entry to remove\r
612\r
613**/\r
614VOID\r
615RemoveMemoryMapEntry (\r
616 MEMORY_MAP *Entry\r
617 )\r
618{\r
619 RemoveEntryList (&Entry->Link);\r
620 Entry->Link.ForwardLink = NULL;\r
621\r
622 if (Entry->FromPages) {\r
623 //\r
624 // Insert the free memory map descriptor to the end of mFreeMemoryMapEntryList\r
625 //\r
626 InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);\r
627 }\r
628}\r
629\r
630\r
631/**\r
632 Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList.\r
633 If the list is emtry, then allocate a new page to refuel the list.\r
634 Please Note this algorithm to allocate the memory map descriptor has a property\r
635 that the memory allocated for memory entries always grows, and will never really be freed\r
636 For example, if the current boot uses 2000 memory map entries at the maximum point, but\r
637 ends up with only 50 at the time the OS is booted, then the memory associated with the 1950\r
638 memory map entries is still allocated from EfiBootServicesMemory.\r
639\r
640\r
641 @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList\r
642\r
643**/\r
644MEMORY_MAP *\r
645AllocateMemoryMapEntry (\r
646 VOID\r
647 )\r
648{\r
649 MEMORY_MAP* FreeDescriptorEntries;\r
650 MEMORY_MAP* Entry;\r
651 UINTN Index;\r
652 \r
653 if (IsListEmpty (&mFreeMemoryMapEntryList)) {\r
654 // \r
655 // The list is empty, to allocate one page to refuel the list\r
656 //\r
657 FreeDescriptorEntries = CoreAllocatePoolPages (EfiBootServicesData, EFI_SIZE_TO_PAGES(DEFAULT_PAGE_ALLOCATION), DEFAULT_PAGE_ALLOCATION);\r
658 if(FreeDescriptorEntries != NULL) {\r
659 //\r
660 // Enque the free memmory map entries into the list\r
661 //\r
662 for (Index = 0; Index< DEFAULT_PAGE_ALLOCATION / sizeof(MEMORY_MAP); Index++) {\r
663 FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE;\r
664 InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link);\r
665 } \r
666 } else {\r
667 return NULL;\r
668 }\r
669 }\r
670 //\r
671 // dequeue the first descriptor from the list\r
672 //\r
673 Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
674 RemoveEntryList (&Entry->Link);\r
675 \r
676 return Entry;\r
677} \r
678\r
679\r
680/**\r
681 Internal function. Converts a memory range to the specified type.\r
682 The range must exist in the memory map.\r
683\r
684 @param Start The first address of the range Must be page \r
685 aligned \r
686 @param NumberOfPages The number of pages to convert \r
687 @param NewType The new type for the memory range \r
688\r
689 @retval EFI_INVALID_PARAMETER Invalid parameter \r
690 @retval EFI_NOT_FOUND Could not find a descriptor cover the specified \r
691 range or convertion not allowed. \r
692 @retval EFI_SUCCESS Successfully converts the memory range to the \r
693 specified type.\r
694\r
695**/\r
696EFI_STATUS\r
697CoreConvertPages (\r
698 IN UINT64 Start,\r
699 IN UINT64 NumberOfPages,\r
700 IN EFI_MEMORY_TYPE NewType\r
701 )\r
702{\r
703\r
704 UINT64 NumberOfBytes;\r
705 UINT64 End;\r
706 UINT64 RangeEnd;\r
707 UINT64 Attribute;\r
708 LIST_ENTRY *Link;\r
709 MEMORY_MAP *Entry;\r
710\r
711 Entry = NULL;\r
712 NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);\r
713 End = Start + NumberOfBytes - 1;\r
714\r
715 ASSERT (NumberOfPages);\r
716 ASSERT ((Start & EFI_PAGE_MASK) == 0);\r
717 ASSERT (End > Start) ;\r
718 ASSERT_LOCKED (&gMemoryLock);\r
719\r
720 if (NumberOfPages == 0 || ((Start & EFI_PAGE_MASK) != 0) || (Start > (Start + NumberOfBytes))) {\r
721 return EFI_INVALID_PARAMETER;\r
722 }\r
723\r
724 //\r
725 // Convert the entire range\r
726 //\r
727\r
728 while (Start < End) {\r
729\r
730 //\r
731 // Find the entry that the covers the range\r
732 //\r
733 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
734 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
735\r
736 if (Entry->Start <= Start && Entry->End > Start) {\r
737 break;\r
738 }\r
739 }\r
740\r
741 if (Link == &gMemoryMap) {\r
742 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: failed to find range %lx - %lx\n", Start, End));\r
743 return EFI_NOT_FOUND;\r
744 }\r
745\r
746 //\r
747 // Convert range to the end, or to the end of the descriptor\r
748 // if that's all we've got\r
749 //\r
750 RangeEnd = End;\r
751 if (Entry->End < End) {\r
752 RangeEnd = Entry->End;\r
753 }\r
754\r
755 DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to %d\n", Start, RangeEnd, NewType));\r
756\r
757 //\r
758 // Debug code - verify conversion is allowed\r
759 //\r
760 if (!(NewType == EfiConventionalMemory ? 1 : 0) ^ (Entry->Type == EfiConventionalMemory ? 1 : 0)) {\r
761 DEBUG ((DEBUG_ERROR , "ConvertPages: Incompatible memory types\n"));\r
762 return EFI_NOT_FOUND;\r
763 } \r
764\r
765 //\r
766 // Update counters for the number of pages allocated to each memory type\r
767 //\r
768 if (Entry->Type >= 0 && Entry->Type < EfiMaxMemoryType) {\r
769 if (Start >= mMemoryTypeStatistics[Entry->Type].BaseAddress && \r
770 Start <= mMemoryTypeStatistics[Entry->Type].MaximumAddress) {\r
771 if (NumberOfPages > mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages) {\r
772 mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages = 0;\r
773 } else {\r
774 mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages -= NumberOfPages;\r
775 }\r
776 }\r
777 }\r
778\r
779 if (NewType >= 0 && NewType < EfiMaxMemoryType) {\r
780 if (Start >= mMemoryTypeStatistics[NewType].BaseAddress && Start <= mMemoryTypeStatistics[NewType].MaximumAddress) {\r
781 mMemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages;\r
782 if (mMemoryTypeStatistics[NewType].CurrentNumberOfPages > \r
783 gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages) {\r
784 gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)mMemoryTypeStatistics[NewType].CurrentNumberOfPages;\r
785 }\r
786 }\r
787 }\r
788\r
789 //\r
790 // Pull range out of descriptor\r
791 //\r
792 if (Entry->Start == Start) {\r
793 \r
794 //\r
795 // Clip start\r
796 //\r
797 Entry->Start = RangeEnd + 1;\r
798\r
799 } else if (Entry->End == RangeEnd) {\r
800 \r
801 //\r
802 // Clip end\r
803 //\r
804 Entry->End = Start - 1;\r
805\r
806 } else {\r
807\r
808 //\r
809 // Pull it out of the center, clip current\r
810 //\r
811 \r
812 //\r
813 // Add a new one\r
814 //\r
815 mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE;\r
816 mMapStack[mMapDepth].FromPages = FALSE;\r
817 mMapStack[mMapDepth].Type = Entry->Type;\r
818 mMapStack[mMapDepth].Start = RangeEnd+1;\r
819 mMapStack[mMapDepth].End = Entry->End;\r
820\r
821 //\r
822 // Inherit Attribute from the Memory Descriptor that is being clipped\r
823 //\r
824 mMapStack[mMapDepth].Attribute = Entry->Attribute;\r
825\r
826 Entry->End = Start - 1;\r
827 ASSERT (Entry->Start < Entry->End);\r
828\r
829 Entry = &mMapStack[mMapDepth];\r
830 InsertTailList (&gMemoryMap, &Entry->Link);\r
831\r
832 mMapDepth += 1;\r
833 ASSERT (mMapDepth < MAX_MAP_DEPTH);\r
834 }\r
835\r
836 //\r
837 // The new range inherits the same Attribute as the Entry \r
838 //it is being cut out of\r
839 //\r
840 Attribute = Entry->Attribute;\r
841\r
842 //\r
843 // If the descriptor is empty, then remove it from the map\r
844 //\r
845 if (Entry->Start == Entry->End + 1) {\r
846 RemoveMemoryMapEntry (Entry);\r
847 Entry = NULL;\r
848 }\r
849 \r
850 //\r
851 // Add our new range in\r
852 //\r
853 CoreAddRange (NewType, Start, RangeEnd, Attribute);\r
854\r
855 //\r
856 // Move any map descriptor stack to general pool\r
857 //\r
858 CoreFreeMemoryMapStack ();\r
859\r
860 //\r
861 // Bump the starting address, and convert the next range\r
862 //\r
863 Start = RangeEnd + 1;\r
864 }\r
865\r
866 //\r
867 // Converted the whole range, done\r
868 //\r
869\r
870 return EFI_SUCCESS;\r
871}\r
872\r
873\r
874\r
875/**\r
876 Internal function. Finds a consecutive free page range below\r
877 the requested address.\r
878\r
879 @param MaxAddress The address that the range must be below \r
880 @param NumberOfPages Number of pages needed \r
881 @param NewType The type of memory the range is going to be \r
882 turned into \r
883 @param Alignment Bits to align with \r
884\r
885 @return The base address of the range, or 0 if the range was not found\r
886\r
887**/\r
888UINT64\r
889CoreFindFreePagesI (\r
890 IN UINT64 MaxAddress,\r
891 IN UINT64 NumberOfPages,\r
892 IN EFI_MEMORY_TYPE NewType,\r
893 IN UINTN Alignment\r
894 )\r
895{\r
896 UINT64 NumberOfBytes;\r
897 UINT64 Target;\r
898 UINT64 DescStart;\r
899 UINT64 DescEnd;\r
900 UINT64 DescNumberOfBytes;\r
901 LIST_ENTRY *Link;\r
902 MEMORY_MAP *Entry;\r
903\r
904 if ((MaxAddress < EFI_PAGE_MASK) ||(NumberOfPages == 0)) {\r
905 return 0;\r
906 }\r
907\r
908 if ((MaxAddress & EFI_PAGE_MASK) != EFI_PAGE_MASK) {\r
909 \r
910 //\r
911 // If MaxAddress is not aligned to the end of a page\r
912 //\r
913 \r
914 //\r
915 // Change MaxAddress to be 1 page lower\r
916 //\r
917 MaxAddress -= (EFI_PAGE_MASK + 1);\r
918 \r
919 //\r
920 // Set MaxAddress to a page boundary\r
921 //\r
922 MaxAddress &= ~EFI_PAGE_MASK;\r
923 \r
924 //\r
925 // Set MaxAddress to end of the page\r
926 //\r
927 MaxAddress |= EFI_PAGE_MASK;\r
928 }\r
929\r
930 NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);\r
931 Target = 0;\r
932\r
933 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
934 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
935 \r
936 //\r
937 // If it's not a free entry, don't bother with it\r
938 //\r
939 if (Entry->Type != EfiConventionalMemory) {\r
940 continue;\r
941 }\r
942\r
943 DescStart = Entry->Start;\r
944 DescEnd = Entry->End;\r
945\r
946 //\r
947 // If desc is past max allowed address, skip it\r
948 //\r
949 if (DescStart >= MaxAddress) {\r
950 continue;\r
951 }\r
952\r
953 //\r
954 // If desc ends past max allowed address, clip the end\r
955 //\r
956 if (DescEnd >= MaxAddress) {\r
957 DescEnd = MaxAddress;\r
958 }\r
959\r
960 DescEnd = ((DescEnd + 1) & (~(Alignment - 1))) - 1;\r
961\r
962 //\r
963 // Compute the number of bytes we can used from this \r
964 // descriptor, and see it's enough to satisfy the request\r
965 //\r
966 DescNumberOfBytes = DescEnd - DescStart + 1;\r
967\r
968 if (DescNumberOfBytes >= NumberOfBytes) {\r
969\r
970 //\r
971 // If this is the best match so far remember it\r
972 //\r
973 if (DescEnd > Target) {\r
974 Target = DescEnd;\r
975 }\r
976 }\r
977 } \r
978\r
979 //\r
980 // If this is a grow down, adjust target to be the allocation base\r
981 //\r
982 Target -= NumberOfBytes - 1;\r
983\r
984 //\r
985 // If we didn't find a match, return 0\r
986 //\r
987 if ((Target & EFI_PAGE_MASK) != 0) {\r
988 return 0;\r
989 }\r
990\r
991 return Target;\r
992}\r
993\r
994\r
995/**\r
996 Internal function. Finds a consecutive free page range below\r
997 the requested address\r
998\r
999 @param MaxAddress The address that the range must be below \r
1000 @param NoPages Number of pages needed \r
1001 @param NewType The type of memory the range is going to be \r
1002 turned into \r
1003 @param Alignment Bits to align with \r
1004\r
1005 @return The base address of the range, or 0 if the range was not found.\r
1006\r
1007**/\r
1008UINT64\r
1009FindFreePages (\r
1010 IN UINT64 MaxAddress,\r
1011 IN UINT64 NoPages,\r
1012 IN EFI_MEMORY_TYPE NewType,\r
1013 IN UINTN Alignment\r
1014 )\r
1015{\r
1016 UINT64 NewMaxAddress;\r
1017 UINT64 Start;\r
1018\r
1019 NewMaxAddress = MaxAddress;\r
1020\r
1021 if (NewType >= 0 && NewType < EfiMaxMemoryType && NewMaxAddress >= mMemoryTypeStatistics[NewType].MaximumAddress) {\r
1022 NewMaxAddress = mMemoryTypeStatistics[NewType].MaximumAddress;\r
1023 } else {\r
1024 if (NewMaxAddress > mDefaultMaximumAddress) {\r
1025 NewMaxAddress = mDefaultMaximumAddress;\r
1026 }\r
1027 }\r
1028\r
1029 Start = CoreFindFreePagesI (NewMaxAddress, NoPages, NewType, Alignment);\r
1030 if (Start == 0) {\r
1031 Start = CoreFindFreePagesI (MaxAddress, NoPages, NewType, Alignment);\r
1032 if (Start == 0) {\r
1033 //\r
1034 // Here means there may be no enough memory to use, so try to go through\r
1035 // all the memory descript to promote the untested memory directly\r
1036 //\r
1037 PromoteMemoryResource ();\r
1038\r
1039 //\r
1040 // Allocate memory again after the memory resource re-arranged\r
1041 //\r
1042 Start = CoreFindFreePagesI (MaxAddress, NoPages, NewType, Alignment);\r
1043 }\r
1044 }\r
1045\r
1046 return Start;\r
1047}\r
1048\r
1049\r
1050\r
1051/**\r
1052 Allocates pages from the memory map.\r
1053\r
1054 @param Type The type of allocation to perform \r
1055 @param MemoryType The type of memory to turn the allocated pages \r
1056 into \r
1057 @param NumberOfPages The number of pages to allocate \r
1058 @param Memory A pointer to receive the base allocated memory \r
1059 address \r
1060\r
1061 @return Status. On success, Memory is filled in with the base address allocated\r
1062 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in \r
1063 spec. \r
1064 @retval EFI_NOT_FOUND Could not allocate pages match the requirement. \r
1065 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate. \r
1066 @retval EFI_SUCCESS Pages successfully allocated.\r
1067\r
1068**/\r
1069EFI_STATUS\r
1070EFIAPI\r
1071CoreAllocatePages (\r
1072 IN EFI_ALLOCATE_TYPE Type,\r
1073 IN EFI_MEMORY_TYPE MemoryType,\r
1074 IN UINTN NumberOfPages,\r
1075 IN OUT EFI_PHYSICAL_ADDRESS *Memory\r
1076 )\r
1077{\r
1078 EFI_STATUS Status;\r
1079 UINT64 Start;\r
1080 UINT64 MaxAddress;\r
1081 UINTN Alignment;\r
1082\r
1083 if (Type < AllocateAnyPages || Type >= (UINTN) MaxAllocateType) {\r
1084 return EFI_INVALID_PARAMETER;\r
1085 }\r
1086\r
1087 if ((MemoryType >= EfiMaxMemoryType && MemoryType <= 0x7fffffff) ||\r
1088 MemoryType == EfiConventionalMemory) {\r
1089 return EFI_INVALID_PARAMETER;\r
1090 }\r
1091\r
1092 Alignment = EFI_DEFAULT_PAGE_ALLOCATION_ALIGNMENT;\r
1093\r
1094 if (MemoryType == EfiACPIReclaimMemory ||\r
1095 MemoryType == EfiACPIMemoryNVS ||\r
1096 MemoryType == EfiRuntimeServicesCode ||\r
1097 MemoryType == EfiRuntimeServicesData) {\r
1098\r
1099 Alignment = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;\r
1100 }\r
1101\r
1102 if (Type == AllocateAddress) {\r
1103 if ((*Memory & (Alignment - 1)) != 0) {\r
1104 return EFI_NOT_FOUND;\r
1105 }\r
1106 }\r
1107\r
1108 NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;\r
1109 NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);\r
1110\r
1111 //\r
1112 // If this is for below a particular address, then \r
1113 //\r
1114 Start = *Memory;\r
1115 \r
1116 //\r
1117 // The max address is the max natively addressable address for the processor\r
1118 //\r
1119 MaxAddress = EFI_MAX_ADDRESS;\r
1120 \r
1121 if (Type == AllocateMaxAddress) {\r
1122 MaxAddress = Start;\r
1123 }\r
1124\r
1125 CoreAcquireMemoryLock ();\r
1126 \r
1127 //\r
1128 // If not a specific address, then find an address to allocate\r
1129 //\r
1130 if (Type != AllocateAddress) {\r
1131 Start = FindFreePages (MaxAddress, NumberOfPages, MemoryType, Alignment);\r
1132 if (Start == 0) {\r
1133 Status = EFI_OUT_OF_RESOURCES;\r
1134 goto Done;\r
1135 }\r
1136 }\r
1137\r
1138 //\r
1139 // Convert pages from FreeMemory to the requested type\r
1140 //\r
1141 Status = CoreConvertPages (Start, NumberOfPages, MemoryType);\r
1142\r
1143Done:\r
1144 CoreReleaseMemoryLock ();\r
1145\r
1146 if (!EFI_ERROR (Status)) {\r
1147 *Memory = Start;\r
1148 }\r
1149\r
1150 return Status;\r
1151}\r
1152\r
1153\r
1154\r
1155\r
1156\r
1157/**\r
1158 Frees previous allocated pages.\r
1159\r
1160 @param Memory Base address of memory being freed \r
1161 @param NumberOfPages The number of pages to free \r
1162\r
1163 @retval EFI_NOT_FOUND Could not find the entry that covers the range \r
1164 @retval EFI_INVALID_PARAMETER Address not aligned \r
1165 @return EFI_SUCCESS -Pages successfully freed.\r
1166\r
1167**/\r
1168EFI_STATUS \r
1169EFIAPI\r
1170CoreFreePages (\r
1171 IN EFI_PHYSICAL_ADDRESS Memory,\r
1172 IN UINTN NumberOfPages\r
1173 )\r
1174{\r
1175 EFI_STATUS Status;\r
1176 LIST_ENTRY *Link;\r
1177 MEMORY_MAP *Entry;\r
1178 UINTN Alignment;\r
1179\r
1180 //\r
1181 // Free the range\r
1182 //\r
1183 CoreAcquireMemoryLock ();\r
1184\r
1185 //\r
1186 // Find the entry that the covers the range\r
1187 //\r
1188 Entry = NULL;\r
1189 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
1190 Entry = CR(Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
1191 if (Entry->Start <= Memory && Entry->End > Memory) {\r
1192 break;\r
1193 }\r
1194 }\r
1195 if (Link == &gMemoryMap) {\r
1196 CoreReleaseMemoryLock ();\r
1197 return EFI_NOT_FOUND;\r
1198 }\r
1199\r
1200 Alignment = EFI_DEFAULT_PAGE_ALLOCATION_ALIGNMENT;\r
1201\r
1202 if (Entry->Type == EfiACPIReclaimMemory ||\r
1203 Entry->Type == EfiACPIMemoryNVS ||\r
1204 Entry->Type == EfiRuntimeServicesCode ||\r
1205 Entry->Type == EfiRuntimeServicesData) {\r
1206\r
1207 Alignment = EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT;\r
1208\r
1209 }\r
1210\r
1211 if ((Memory & (Alignment - 1)) != 0) {\r
1212 CoreReleaseMemoryLock ();\r
1213 return EFI_INVALID_PARAMETER;\r
1214 }\r
1215\r
1216 NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;\r
1217 NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);\r
1218\r
1219 Status = CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);\r
1220\r
1221 CoreReleaseMemoryLock ();\r
1222\r
1223 if (EFI_ERROR (Status)) {\r
1224 return Status;\r
1225 }\r
1226\r
1227 //\r
1228 // Destroy the contents\r
1229 //\r
1230 if (Memory < EFI_MAX_ADDRESS) {\r
1231 DEBUG_CLEAR_MEMORY ((VOID *)(UINTN)Memory, NumberOfPages << EFI_PAGE_SHIFT);\r
1232 }\r
1233 \r
1234 return Status;\r
1235}\r
1236\r
1237\r
1238\r
1239\r
1240/**\r
1241 This function returns a copy of the current memory map. The map is an array of\r
1242 memory descriptors, each of which describes a contiguous block of memory.\r
1243\r
1244 @param MemoryMapSize A pointer to the size, in bytes, of the \r
1245 MemoryMap buffer. On input, this is the size of \r
1246 the buffer allocated by the caller. On output, \r
1247 it is the size of the buffer returned by the \r
1248 firmware if the buffer was large enough, or the \r
1249 size of the buffer needed to contain the map if \r
1250 the buffer was too small. \r
1251 @param MemoryMap A pointer to the buffer in which firmware places \r
1252 the current memory map. \r
1253 @param MapKey A pointer to the location in which firmware \r
1254 returns the key for the current memory map. \r
1255 @param DescriptorSize A pointer to the location in which firmware \r
1256 returns the size, in bytes, of an individual \r
1257 EFI_MEMORY_DESCRIPTOR. \r
1258 @param DescriptorVersion A pointer to the location in which firmware \r
1259 returns the version number associated with the \r
1260 EFI_MEMORY_DESCRIPTOR. \r
1261\r
1262 @retval EFI_SUCCESS The memory map was returned in the MemoryMap \r
1263 buffer. \r
1264 @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current \r
1265 buffer size needed to hold the memory map is \r
1266 returned in MemoryMapSize. \r
1267 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.\r
1268\r
1269**/\r
1270EFI_STATUS\r
1271EFIAPI\r
1272CoreGetMemoryMap (\r
1273 IN OUT UINTN *MemoryMapSize,\r
1274 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,\r
1275 OUT UINTN *MapKey,\r
1276 OUT UINTN *DescriptorSize,\r
1277 OUT UINT32 *DescriptorVersion\r
1278 )\r
1279{\r
1280 EFI_STATUS Status;\r
1281 UINTN Size; \r
1282 UINTN BufferSize; \r
1283 UINTN NumberOfRuntimeEntries;\r
1284 LIST_ENTRY *Link;\r
1285 MEMORY_MAP *Entry; \r
1286 EFI_GCD_MAP_ENTRY *GcdMapEntry; \r
1287 EFI_MEMORY_TYPE Type;\r
1288\r
1289 //\r
1290 // Make sure the parameters are valid\r
1291 //\r
1292 if (MemoryMapSize == NULL) {\r
1293 return EFI_INVALID_PARAMETER;\r
1294 }\r
1295 \r
1296 CoreAcquireGcdMemoryLock ();\r
1297 \r
1298 //\r
1299 // Count the number of Reserved and MMIO entries that are marked for runtime use\r
1300 //\r
1301 NumberOfRuntimeEntries = 0;\r
1302 for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) {\r
1303 GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);\r
1304 if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) ||\r
1305 (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo)) {\r
1306 if ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) {\r
1307 NumberOfRuntimeEntries++;\r
1308 }\r
1309 }\r
1310 }\r
1311\r
1312 Size = sizeof (EFI_MEMORY_DESCRIPTOR);\r
1313\r
1314 //\r
1315 // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will\r
1316 // prevent people from having pointer math bugs in their code.\r
1317 // now you have to use *DescriptorSize to make things work.\r
1318 //\r
1319 Size += sizeof(UINT64) - (Size % sizeof (UINT64));\r
1320\r
1321 if (DescriptorSize != NULL) {\r
1322 *DescriptorSize = Size;\r
1323 }\r
1324 \r
1325 if (DescriptorVersion != NULL) {\r
1326 *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION;\r
1327 }\r
1328\r
1329 CoreAcquireMemoryLock ();\r
1330\r
1331 //\r
1332 // Compute the buffer size needed to fit the entire map\r
1333 //\r
1334 BufferSize = Size * NumberOfRuntimeEntries;\r
1335 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
1336 BufferSize += Size;\r
1337 }\r
1338\r
1339 if (*MemoryMapSize < BufferSize) {\r
1340 Status = EFI_BUFFER_TOO_SMALL;\r
1341 goto Done;\r
1342 }\r
1343\r
1344 if (MemoryMap == NULL) {\r
1345 Status = EFI_INVALID_PARAMETER;\r
1346 goto Done;\r
1347 }\r
1348\r
1349 //\r
1350 // Build the map\r
1351 //\r
1352 ZeroMem (MemoryMap, BufferSize);\r
1353 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
1354 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
1355 ASSERT (Entry->VirtualStart == 0);\r
1356\r
1357 //\r
1358 // Convert internal map into an EFI_MEMORY_DESCRIPTOR\r
1359 //\r
1360 MemoryMap->Type = Entry->Type;\r
1361 MemoryMap->PhysicalStart = Entry->Start;\r
1362 MemoryMap->VirtualStart = Entry->VirtualStart;\r
1363 MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);\r
1364 //\r
1365 // If the memory type is EfiConventionalMemory, then determine if the range is part of a\r
1366 // memory type bin and needs to be converted to the same memory type as the rest of the \r
1367 // memory type bin in order to minimize EFI Memory Map changes across reboots. This \r
1368 // improves the chances for a successful S4 resume in the presence of minor page allocation\r
1369 // differences across reboots.\r
1370 //\r
1371 if (MemoryMap->Type == EfiConventionalMemory) {\r
1372 for (Type = (EFI_MEMORY_TYPE) 0; Type < EfiMaxMemoryType; Type++) {\r
1373 if (mMemoryTypeStatistics[Type].Special &&\r
1374 mMemoryTypeStatistics[Type].NumberOfPages > 0 &&\r
1375 Entry->Start >= mMemoryTypeStatistics[Type].BaseAddress &&\r
1376 Entry->End <= mMemoryTypeStatistics[Type].MaximumAddress ) {\r
1377 MemoryMap->Type = Type;\r
1378 }\r
1379 }\r
1380 }\r
1381 MemoryMap->Attribute = Entry->Attribute;\r
1382 if (mMemoryTypeStatistics[MemoryMap->Type].Runtime) {\r
1383 MemoryMap->Attribute |= EFI_MEMORY_RUNTIME;\r
1384 }\r
1385 \r
1386 MemoryMap = NextMemoryDescriptor (MemoryMap, Size);\r
1387 }\r
1388\r
1389 for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) {\r
1390 GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);\r
1391 if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) ||\r
1392 (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo)) {\r
1393 if ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME) {\r
1394 \r
1395 MemoryMap->PhysicalStart = GcdMapEntry->BaseAddress;\r
1396 MemoryMap->VirtualStart = 0;\r
1397 MemoryMap->NumberOfPages = RShiftU64 ((GcdMapEntry->EndAddress - GcdMapEntry->BaseAddress + 1), EFI_PAGE_SHIFT);\r
1398 MemoryMap->Attribute = GcdMapEntry->Attributes & ~EFI_MEMORY_PORT_IO;\r
1399\r
1400 if (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) {\r
1401 MemoryMap->Type = EfiReservedMemoryType;\r
1402 } else if (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {\r
1403 if ((GcdMapEntry->Attributes & EFI_MEMORY_PORT_IO) == EFI_MEMORY_PORT_IO) {\r
1404 MemoryMap->Type = EfiMemoryMappedIOPortSpace;\r
1405 } else {\r
1406 MemoryMap->Type = EfiMemoryMappedIO;\r
1407 }\r
1408 }\r
1409\r
1410 MemoryMap = NextMemoryDescriptor (MemoryMap, Size);\r
1411 }\r
1412 }\r
1413 }\r
1414 \r
1415 Status = EFI_SUCCESS;\r
1416\r
1417Done:\r
1418\r
1419 CoreReleaseMemoryLock ();\r
1420 \r
1421 CoreReleaseGcdMemoryLock ();\r
1422 \r
1423 // \r
1424 // Update the map key finally \r
1425 // \r
1426 if (MapKey != NULL) {\r
1427 *MapKey = mMemoryMapKey;\r
1428 }\r
1429 \r
1430 *MemoryMapSize = BufferSize;\r
1431 \r
1432 return Status;\r
1433}\r
1434\r
1435\r
1436/**\r
1437 Internal function. Used by the pool functions to allocate pages\r
1438 to back pool allocation requests.\r
1439\r
1440 @param PoolType The type of memory for the new pool pages \r
1441 @param NumberOfPages No of pages to allocate \r
1442 @param Alignment Bits to align. \r
1443\r
1444 @return The allocated memory, or NULL\r
1445\r
1446**/\r
1447VOID *\r
1448CoreAllocatePoolPages (\r
1449 IN EFI_MEMORY_TYPE PoolType,\r
1450 IN UINTN NumberOfPages,\r
1451 IN UINTN Alignment\r
1452 )\r
1453{\r
1454 UINT64 Start;\r
1455\r
1456 //\r
1457 // Find the pages to convert\r
1458 //\r
1459 Start = FindFreePages (EFI_MAX_ADDRESS, NumberOfPages, PoolType, Alignment);\r
1460\r
1461 //\r
1462 // Convert it to boot services data\r
1463 //\r
1464 if (Start == 0) {\r
1465 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "AllocatePoolPages: failed to allocate %d pages\n", NumberOfPages));\r
1466 } else {\r
1467 CoreConvertPages (Start, NumberOfPages, PoolType);\r
1468 }\r
1469\r
1470 return (VOID *)(UINTN)Start;\r
1471}\r
1472\r
1473\r
1474/**\r
1475 Internal function. Frees pool pages allocated via AllocatePoolPages ()\r
1476\r
1477 @param Memory The base address to free \r
1478 @param NumberOfPages The number of pages to free\r
1479\r
1480**/\r
1481VOID\r
1482CoreFreePoolPages (\r
1483 IN EFI_PHYSICAL_ADDRESS Memory,\r
1484 IN UINTN NumberOfPages\r
1485 )\r
1486{\r
1487 CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);\r
1488}\r
1489\r
1490\r
1491\r
1492/**\r
1493 Make sure the memory map is following all the construction rules,\r
1494 it is the last time to check memory map error before exit boot services.\r
1495\r
1496 @param MapKey Memory map key \r
1497\r
1498 @retval EFI_INVALID_PARAMETER Memory map not consistent with construction \r
1499 rules. \r
1500 @retval EFI_SUCCESS Valid memory map.\r
1501\r
1502**/\r
1503EFI_STATUS\r
1504CoreTerminateMemoryMap (\r
1505 IN UINTN MapKey\r
1506 )\r
1507{\r
1508 EFI_STATUS Status;\r
1509 LIST_ENTRY *Link;\r
1510 MEMORY_MAP *Entry;\r
1511\r
1512 Status = EFI_SUCCESS;\r
1513\r
1514 CoreAcquireMemoryLock ();\r
1515\r
1516 if (MapKey == mMemoryMapKey) {\r
1517\r
1518 //\r
1519 // Make sure the memory map is following all the construction rules\r
1520 // This is the last chance we will be able to display any messages on\r
1521 // the console devices.\r
1522 //\r
1523\r
1524 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
1525 Entry = CR(Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
1526 if (Entry->Attribute & EFI_MEMORY_RUNTIME) { \r
1527 if (Entry->Type == EfiACPIReclaimMemory || Entry->Type == EfiACPIMemoryNVS) {\r
1528 DEBUG((DEBUG_ERROR, "ExitBootServices: ACPI memory entry has RUNTIME attribute set.\n"));\r
1529 CoreReleaseMemoryLock ();\r
1530 return EFI_INVALID_PARAMETER;\r
1531 }\r
1532 if (Entry->Start & (EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT - 1)) {\r
1533 DEBUG((DEBUG_ERROR, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n"));\r
1534 CoreReleaseMemoryLock ();\r
1535 return EFI_INVALID_PARAMETER;\r
1536 }\r
1537 if ((Entry->End + 1) & (EFI_ACPI_RUNTIME_PAGE_ALLOCATION_ALIGNMENT - 1)) {\r
1538 DEBUG((DEBUG_ERROR, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n"));\r
1539 CoreReleaseMemoryLock ();\r
1540 return EFI_INVALID_PARAMETER;\r
1541 }\r
1542 }\r
1543 }\r
1544\r
1545 //\r
1546 // The map key they gave us matches what we expect. Fall through and\r
1547 // return success. In an ideal world we would clear out all of\r
1548 // EfiBootServicesCode and EfiBootServicesData. However this function\r
1549 // is not the last one called by ExitBootServices(), so we have to\r
1550 // preserve the memory contents.\r
1551 //\r
1552 } else {\r
1553 Status = EFI_INVALID_PARAMETER;\r
1554 }\r
1555\r
1556 CoreReleaseMemoryLock ();\r
1557\r
1558 return Status;\r
1559}\r
1560\r
1561\r
1562\r
1563\r
1564\r
1565\r
1566\r
1567\r
1568\r