]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Core/Dxe/Mem/Page.c
MdeModulePkg: Clean up source files
[mirror_edk2.git] / MdeModulePkg / Core / Dxe / Mem / Page.c
CommitLineData
504214c4 1/** @file\r
504214c4
LG
2 UEFI Memory page management functions.\r
3\r
d1102dba 4Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.<BR>\r
cd5ebaa0 5This program and the accompanying materials\r
23c98c94 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
28a00297 12\r
504214c4 13**/\r
28a00297 14\r
9c4ac31c 15#include "DxeMain.h"\r
ec90508b 16#include "Imem.h"\r
e63da9f0 17#include "HeapGuard.h"\r
28a00297 18\r
28a00297 19//\r
d45fd260 20// Entry for tracking the memory regions for each memory type to coalesce similar memory types\r
28a00297 21//\r
22typedef struct {\r
23 EFI_PHYSICAL_ADDRESS BaseAddress;\r
24 EFI_PHYSICAL_ADDRESS MaximumAddress;\r
25 UINT64 CurrentNumberOfPages;\r
b74350e9 26 UINT64 NumberOfPages;\r
28a00297 27 UINTN InformationIndex;\r
b74350e9 28 BOOLEAN Special;\r
29 BOOLEAN Runtime;\r
d613c2a8 30} EFI_MEMORY_TYPE_STATISTICS;\r
28a00297 31\r
32//\r
33// MemoryMap - The current memory map\r
34//\r
35UINTN mMemoryMapKey = 0;\r
36\r
28a00297 37#define MAX_MAP_DEPTH 6\r
dc8d93ca 38\r
39///\r
40/// mMapDepth - depth of new descriptor stack\r
41///\r
28a00297 42UINTN mMapDepth = 0;\r
dc8d93ca 43///\r
44/// mMapStack - space to use as temp storage to build new map descriptors\r
45///\r
28a00297 46MEMORY_MAP mMapStack[MAX_MAP_DEPTH];\r
47UINTN mFreeMapStack = 0;\r
dc8d93ca 48///\r
49/// This list maintain the free memory map list\r
50///\r
e94a9ff7 51LIST_ENTRY mFreeMemoryMapEntryList = INITIALIZE_LIST_HEAD_VARIABLE (mFreeMemoryMapEntryList);\r
52BOOLEAN mMemoryTypeInformationInitialized = FALSE;\r
28a00297 53\r
d613c2a8 54EFI_MEMORY_TYPE_STATISTICS mMemoryTypeStatistics[EfiMaxMemoryType + 1] = {\r
f3f2e05d 55 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiReservedMemoryType\r
56 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderCode\r
57 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiLoaderData\r
58 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesCode\r
59 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiBootServicesData\r
60 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesCode\r
61 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiRuntimeServicesData\r
62 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiConventionalMemory\r
63 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiUnusableMemory\r
64 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIReclaimMemory\r
65 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, FALSE }, // EfiACPIMemoryNVS\r
66 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIO\r
67 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiMemoryMappedIOPortSpace\r
68 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, TRUE, TRUE }, // EfiPalCode\r
a671a012 69 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE }, // EfiPersistentMemory\r
f3f2e05d 70 { 0, MAX_ADDRESS, 0, 0, EfiMaxMemoryType, FALSE, FALSE } // EfiMaxMemoryType\r
28a00297 71};\r
72\r
f3f2e05d 73EFI_PHYSICAL_ADDRESS mDefaultMaximumAddress = MAX_ADDRESS;\r
2345e7d4 74EFI_PHYSICAL_ADDRESS mDefaultBaseAddress = MAX_ADDRESS;\r
28a00297 75\r
76EFI_MEMORY_TYPE_INFORMATION gMemoryTypeInformation[EfiMaxMemoryType + 1] = {\r
77 { EfiReservedMemoryType, 0 },\r
78 { EfiLoaderCode, 0 },\r
79 { EfiLoaderData, 0 },\r
80 { EfiBootServicesCode, 0 },\r
81 { EfiBootServicesData, 0 },\r
82 { EfiRuntimeServicesCode, 0 },\r
83 { EfiRuntimeServicesData, 0 },\r
84 { EfiConventionalMemory, 0 },\r
85 { EfiUnusableMemory, 0 },\r
86 { EfiACPIReclaimMemory, 0 },\r
87 { EfiACPIMemoryNVS, 0 },\r
88 { EfiMemoryMappedIO, 0 },\r
89 { EfiMemoryMappedIOPortSpace, 0 },\r
90 { EfiPalCode, 0 },\r
a671a012 91 { EfiPersistentMemory, 0 },\r
28a00297 92 { EfiMaxMemoryType, 0 }\r
93};\r
54ea99a7 94//\r
95// Only used when load module at fixed address feature is enabled. True means the memory is alreay successfully allocated\r
d1102dba 96// and ready to load the module in to specified address.or else, the memory is not ready and module will be loaded at a\r
54ea99a7 97// address assigned by DXE core.\r
98//\r
99GLOBAL_REMOVE_IF_UNREFERENCED BOOLEAN gLoadFixedAddressCodeMemoryReady = FALSE;\r
d9177625 100\r
162ed594 101/**\r
d9177625 102 Enter critical section by gaining lock on gMemoryLock.\r
162ed594 103\r
104**/\r
23c98c94 105VOID\r
d9177625 106CoreAcquireMemoryLock (\r
107 VOID\r
108 )\r
109{\r
110 CoreAcquireLock (&gMemoryLock);\r
111}\r
112\r
113\r
114\r
115/**\r
116 Exit critical section by releasing lock on gMemoryLock.\r
117\r
118**/\r
119VOID\r
120CoreReleaseMemoryLock (\r
28a00297 121 VOID\r
d9177625 122 )\r
123{\r
124 CoreReleaseLock (&gMemoryLock);\r
125}\r
126\r
127\r
128\r
129\r
130/**\r
131 Internal function. Removes a descriptor entry.\r
132\r
133 @param Entry The entry to remove\r
134\r
135**/\r
136VOID\r
137RemoveMemoryMapEntry (\r
138 IN OUT MEMORY_MAP *Entry\r
139 )\r
140{\r
141 RemoveEntryList (&Entry->Link);\r
142 Entry->Link.ForwardLink = NULL;\r
143\r
144 if (Entry->FromPages) {\r
145 //\r
146 // Insert the free memory map descriptor to the end of mFreeMemoryMapEntryList\r
147 //\r
148 InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);\r
149 }\r
150}\r
162ed594 151\r
152/**\r
153 Internal function. Adds a ranges to the memory map.\r
154 The range must not already exist in the map.\r
155\r
022c6d45 156 @param Type The type of memory range to add\r
157 @param Start The starting address in the memory range Must be\r
158 paged aligned\r
159 @param End The last address in the range Must be the last\r
160 byte of a page\r
161 @param Attribute The attributes of the memory range to add\r
28a00297 162\r
162ed594 163**/\r
28a00297 164VOID\r
165CoreAddRange (\r
166 IN EFI_MEMORY_TYPE Type,\r
167 IN EFI_PHYSICAL_ADDRESS Start,\r
168 IN EFI_PHYSICAL_ADDRESS End,\r
169 IN UINT64 Attribute\r
d9177625 170 )\r
171{\r
172 LIST_ENTRY *Link;\r
173 MEMORY_MAP *Entry;\r
28a00297 174\r
d9177625 175 ASSERT ((Start & EFI_PAGE_MASK) == 0);\r
176 ASSERT (End > Start) ;\r
162ed594 177\r
d9177625 178 ASSERT_LOCKED (&gMemoryLock);\r
28a00297 179\r
d9177625 180 DEBUG ((DEBUG_PAGE, "AddRange: %lx-%lx to %d\n", Start, End, Type));\r
d1102dba
LG
181\r
182 //\r
183 // If memory of type EfiConventionalMemory is being added that includes the page\r
184 // starting at address 0, then zero the page starting at address 0. This has\r
185 // two benifits. It helps find NULL pointer bugs and it also maximizes\r
186 // compatibility with operating systems that may evaluate memory in this page\r
187 // for legacy data structures. If memory of any other type is added starting\r
188 // at address 0, then do not zero the page at address 0 because the page is being\r
d436d5ca 189 // used for other purposes.\r
d1102dba 190 //\r
d436d5ca 191 if (Type == EfiConventionalMemory && Start == 0 && (End >= EFI_PAGE_SIZE - 1)) {\r
a7181d95
JW
192 if ((PcdGet8 (PcdNullPointerDetectionPropertyMask) & BIT0) == 0) {\r
193 SetMem ((VOID *)(UINTN)Start, EFI_PAGE_SIZE, 0);\r
194 }\r
d436d5ca 195 }\r
d1102dba 196\r
d9177625 197 //\r
198 // Memory map being altered so updated key\r
199 //\r
200 mMemoryMapKey += 1;\r
162ed594 201\r
d9177625 202 //\r
203 // UEFI 2.0 added an event group for notificaiton on memory map changes.\r
204 // So we need to signal this Event Group every time the memory map changes.\r
205 // If we are in EFI 1.10 compatability mode no event groups will be\r
206 // found and nothing will happen we we call this function. These events\r
207 // will get signaled but since a lock is held around the call to this\r
6393d9c8 208 // function the notificaiton events will only be called after this function\r
d9177625 209 // returns and the lock is released.\r
210 //\r
211 CoreNotifySignalList (&gEfiEventMemoryMapChangeGuid);\r
162ed594 212\r
d9177625 213 //\r
214 // Look for adjoining memory descriptor\r
215 //\r
28a00297 216\r
d9177625 217 // Two memory descriptors can only be merged if they have the same Type\r
218 // and the same Attribute\r
219 //\r
162ed594 220\r
d9177625 221 Link = gMemoryMap.ForwardLink;\r
222 while (Link != &gMemoryMap) {\r
223 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
224 Link = Link->ForwardLink;\r
162ed594 225\r
d9177625 226 if (Entry->Type != Type) {\r
227 continue;\r
228 }\r
229\r
230 if (Entry->Attribute != Attribute) {\r
231 continue;\r
232 }\r
233\r
234 if (Entry->End + 1 == Start) {\r
235\r
236 Start = Entry->Start;\r
237 RemoveMemoryMapEntry (Entry);\r
238\r
239 } else if (Entry->Start == End + 1) {\r
240\r
241 End = Entry->End;\r
242 RemoveMemoryMapEntry (Entry);\r
243 }\r
244 }\r
245\r
246 //\r
247 // Add descriptor\r
248 //\r
249\r
250 mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE;\r
251 mMapStack[mMapDepth].FromPages = FALSE;\r
252 mMapStack[mMapDepth].Type = Type;\r
253 mMapStack[mMapDepth].Start = Start;\r
254 mMapStack[mMapDepth].End = End;\r
255 mMapStack[mMapDepth].VirtualStart = 0;\r
256 mMapStack[mMapDepth].Attribute = Attribute;\r
257 InsertTailList (&gMemoryMap, &mMapStack[mMapDepth].Link);\r
258\r
259 mMapDepth += 1;\r
260 ASSERT (mMapDepth < MAX_MAP_DEPTH);\r
261\r
262 return ;\r
263}\r
022c6d45 264\r
162ed594 265/**\r
266 Internal function. Deque a descriptor entry from the mFreeMemoryMapEntryList.\r
267 If the list is emtry, then allocate a new page to refuel the list.\r
268 Please Note this algorithm to allocate the memory map descriptor has a property\r
269 that the memory allocated for memory entries always grows, and will never really be freed\r
270 For example, if the current boot uses 2000 memory map entries at the maximum point, but\r
271 ends up with only 50 at the time the OS is booted, then the memory associated with the 1950\r
272 memory map entries is still allocated from EfiBootServicesMemory.\r
273\r
274\r
275 @return The Memory map descriptor dequed from the mFreeMemoryMapEntryList\r
276\r
277**/\r
28a00297 278MEMORY_MAP *\r
279AllocateMemoryMapEntry (\r
280 VOID\r
d9177625 281 )\r
282{\r
283 MEMORY_MAP* FreeDescriptorEntries;\r
284 MEMORY_MAP* Entry;\r
285 UINTN Index;\r
286\r
287 if (IsListEmpty (&mFreeMemoryMapEntryList)) {\r
288 //\r
289 // The list is empty, to allocate one page to refuel the list\r
290 //\r
e63da9f0
JW
291 FreeDescriptorEntries = CoreAllocatePoolPages (\r
292 EfiBootServicesData,\r
d4731a98 293 EFI_SIZE_TO_PAGES (DEFAULT_PAGE_ALLOCATION_GRANULARITY),\r
e63da9f0
JW
294 DEFAULT_PAGE_ALLOCATION_GRANULARITY,\r
295 FALSE\r
296 );\r
d4731a98 297 if (FreeDescriptorEntries != NULL) {\r
d9177625 298 //\r
299 // Enque the free memmory map entries into the list\r
300 //\r
d4731a98 301 for (Index = 0; Index < DEFAULT_PAGE_ALLOCATION_GRANULARITY / sizeof(MEMORY_MAP); Index++) {\r
d9177625 302 FreeDescriptorEntries[Index].Signature = MEMORY_MAP_SIGNATURE;\r
303 InsertTailList (&mFreeMemoryMapEntryList, &FreeDescriptorEntries[Index].Link);\r
304 }\r
305 } else {\r
306 return NULL;\r
307 }\r
308 }\r
309 //\r
310 // dequeue the first descriptor from the list\r
311 //\r
312 Entry = CR (mFreeMemoryMapEntryList.ForwardLink, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
313 RemoveEntryList (&Entry->Link);\r
314\r
315 return Entry;\r
316}\r
022c6d45 317\r
162ed594 318\r
319/**\r
d9177625 320 Internal function. Moves any memory descriptors that are on the\r
321 temporary descriptor stack to heap.\r
162ed594 322\r
323**/\r
28a00297 324VOID\r
d9177625 325CoreFreeMemoryMapStack (\r
28a00297 326 VOID\r
327 )\r
28a00297 328{\r
d9177625 329 MEMORY_MAP *Entry;\r
330 MEMORY_MAP *Entry2;\r
331 LIST_ENTRY *Link2;\r
28a00297 332\r
d9177625 333 ASSERT_LOCKED (&gMemoryLock);\r
28a00297 334\r
d9177625 335 //\r
336 // If already freeing the map stack, then return\r
337 //\r
338 if (mFreeMapStack != 0) {\r
339 return ;\r
340 }\r
162ed594 341\r
d9177625 342 //\r
343 // Move the temporary memory descriptor stack into pool\r
344 //\r
345 mFreeMapStack += 1;\r
162ed594 346\r
d9177625 347 while (mMapDepth != 0) {\r
348 //\r
349 // Deque an memory map entry from mFreeMemoryMapEntryList\r
350 //\r
351 Entry = AllocateMemoryMapEntry ();\r
352\r
353 ASSERT (Entry);\r
354\r
355 //\r
356 // Update to proper entry\r
357 //\r
358 mMapDepth -= 1;\r
359\r
360 if (mMapStack[mMapDepth].Link.ForwardLink != NULL) {\r
361\r
362 //\r
363 // Move this entry to general memory\r
364 //\r
365 RemoveEntryList (&mMapStack[mMapDepth].Link);\r
366 mMapStack[mMapDepth].Link.ForwardLink = NULL;\r
367\r
368 CopyMem (Entry , &mMapStack[mMapDepth], sizeof (MEMORY_MAP));\r
369 Entry->FromPages = TRUE;\r
370\r
371 //\r
372 // Find insertion location\r
373 //\r
374 for (Link2 = gMemoryMap.ForwardLink; Link2 != &gMemoryMap; Link2 = Link2->ForwardLink) {\r
375 Entry2 = CR (Link2, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
376 if (Entry2->FromPages && Entry2->Start > Entry->Start) {\r
377 break;\r
378 }\r
379 }\r
380\r
381 InsertTailList (Link2, &Entry->Link);\r
382\r
383 } else {\r
384 //\r
385 // This item of mMapStack[mMapDepth] has already been dequeued from gMemoryMap list,\r
386 // so here no need to move it to memory.\r
387 //\r
388 InsertTailList (&mFreeMemoryMapEntryList, &Entry->Link);\r
389 }\r
390 }\r
28a00297 391\r
d9177625 392 mFreeMapStack -= 1;\r
393}\r
162ed594 394\r
395/**\r
396 Find untested but initialized memory regions in GCD map and convert them to be DXE allocatable.\r
397\r
398**/\r
2345e7d4 399BOOLEAN\r
28a00297 400PromoteMemoryResource (\r
401 VOID\r
402 )\r
28a00297 403{\r
2345e7d4 404 LIST_ENTRY *Link;\r
405 EFI_GCD_MAP_ENTRY *Entry;\r
406 BOOLEAN Promoted;\r
28a00297 407\r
d45fd260 408 DEBUG ((DEBUG_PAGE, "Promote the memory resource\n"));\r
022c6d45 409\r
28a00297 410 CoreAcquireGcdMemoryLock ();\r
022c6d45 411\r
2345e7d4 412 Promoted = FALSE;\r
28a00297 413 Link = mGcdMemorySpaceMap.ForwardLink;\r
414 while (Link != &mGcdMemorySpaceMap) {\r
415\r
416 Entry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);\r
417\r
418 if (Entry->GcdMemoryType == EfiGcdMemoryTypeReserved &&\r
f3f2e05d 419 Entry->EndAddress < MAX_ADDRESS &&\r
28a00297 420 (Entry->Capabilities & (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED)) ==\r
421 (EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED)) {\r
422 //\r
423 // Update the GCD map\r
424 //\r
74705ca5
SZ
425 if ((Entry->Capabilities & EFI_MEMORY_MORE_RELIABLE) == EFI_MEMORY_MORE_RELIABLE) {\r
426 Entry->GcdMemoryType = EfiGcdMemoryTypeMoreReliable;\r
427 } else {\r
428 Entry->GcdMemoryType = EfiGcdMemoryTypeSystemMemory;\r
429 }\r
28a00297 430 Entry->Capabilities |= EFI_MEMORY_TESTED;\r
431 Entry->ImageHandle = gDxeCoreImageHandle;\r
432 Entry->DeviceHandle = NULL;\r
433\r
434 //\r
435 // Add to allocable system memory resource\r
022c6d45 436 //\r
28a00297 437\r
438 CoreAddRange (\r
022c6d45 439 EfiConventionalMemory,\r
440 Entry->BaseAddress,\r
441 Entry->EndAddress,\r
28a00297 442 Entry->Capabilities & ~(EFI_MEMORY_PRESENT | EFI_MEMORY_INITIALIZED | EFI_MEMORY_TESTED | EFI_MEMORY_RUNTIME)\r
443 );\r
444 CoreFreeMemoryMapStack ();\r
022c6d45 445\r
2345e7d4 446 Promoted = TRUE;\r
28a00297 447 }\r
448\r
449 Link = Link->ForwardLink;\r
450 }\r
022c6d45 451\r
28a00297 452 CoreReleaseGcdMemoryLock ();\r
022c6d45 453\r
2345e7d4 454 return Promoted;\r
28a00297 455}\r
54ea99a7 456/**\r
d1102dba
LG
457 This function try to allocate Runtime code & Boot time code memory range. If LMFA enabled, 2 patchable PCD\r
458 PcdLoadFixAddressRuntimeCodePageNumber & PcdLoadFixAddressBootTimeCodePageNumber which are set by tools will record the\r
54ea99a7 459 size of boot time and runtime code.\r
28a00297 460\r
54ea99a7 461**/\r
462VOID\r
463CoreLoadingFixedAddressHook (\r
464 VOID\r
465 )\r
466{\r
467 UINT32 RuntimeCodePageNumber;\r
468 UINT32 BootTimeCodePageNumber;\r
469 EFI_PHYSICAL_ADDRESS RuntimeCodeBase;\r
470 EFI_PHYSICAL_ADDRESS BootTimeCodeBase;\r
471 EFI_STATUS Status;\r
472\r
473 //\r
474 // Make sure these 2 areas are not initialzied.\r
475 //\r
d1102dba 476 if (!gLoadFixedAddressCodeMemoryReady) {\r
54ea99a7 477 RuntimeCodePageNumber = PcdGet32(PcdLoadFixAddressRuntimeCodePageNumber);\r
478 BootTimeCodePageNumber= PcdGet32(PcdLoadFixAddressBootTimeCodePageNumber);\r
479 RuntimeCodeBase = (EFI_PHYSICAL_ADDRESS)(gLoadModuleAtFixAddressConfigurationTable.DxeCodeTopAddress - EFI_PAGES_TO_SIZE (RuntimeCodePageNumber));\r
480 BootTimeCodeBase = (EFI_PHYSICAL_ADDRESS)(RuntimeCodeBase - EFI_PAGES_TO_SIZE (BootTimeCodePageNumber));\r
481 //\r
482 // Try to allocate runtime memory.\r
483 //\r
484 Status = CoreAllocatePages (\r
485 AllocateAddress,\r
486 EfiRuntimeServicesCode,\r
487 RuntimeCodePageNumber,\r
488 &RuntimeCodeBase\r
489 );\r
490 if (EFI_ERROR(Status)) {\r
491 //\r
d1102dba 492 // Runtime memory allocation failed\r
54ea99a7 493 //\r
494 return;\r
495 }\r
496 //\r
497 // Try to allocate boot memory.\r
498 //\r
499 Status = CoreAllocatePages (\r
500 AllocateAddress,\r
501 EfiBootServicesCode,\r
502 BootTimeCodePageNumber,\r
503 &BootTimeCodeBase\r
504 );\r
505 if (EFI_ERROR(Status)) {\r
506 //\r
d1102dba
LG
507 // boot memory allocation failed. Free Runtime code range and will try the allocation again when\r
508 // new memory range is installed.\r
509 //\r
510 CoreFreePages (\r
54ea99a7 511 RuntimeCodeBase,\r
512 RuntimeCodePageNumber\r
513 );\r
514 return;\r
515 }\r
516 gLoadFixedAddressCodeMemoryReady = TRUE;\r
d1102dba 517 }\r
54ea99a7 518 return;\r
d1102dba 519}\r
28a00297 520\r
162ed594 521/**\r
28a00297 522 Called to initialize the memory map and add descriptors to\r
523 the current descriptor list.\r
28a00297 524 The first descriptor that is added must be general usable\r
525 memory as the addition allocates heap.\r
526\r
022c6d45 527 @param Type The type of memory to add\r
528 @param Start The starting address in the memory range Must be\r
529 page aligned\r
530 @param NumberOfPages The number of pages in the range\r
531 @param Attribute Attributes of the memory to add\r
28a00297 532\r
162ed594 533 @return None. The range is added to the memory map\r
28a00297 534\r
162ed594 535**/\r
536VOID\r
537CoreAddMemoryDescriptor (\r
538 IN EFI_MEMORY_TYPE Type,\r
539 IN EFI_PHYSICAL_ADDRESS Start,\r
540 IN UINT64 NumberOfPages,\r
541 IN UINT64 Attribute\r
542 )\r
28a00297 543{\r
544 EFI_PHYSICAL_ADDRESS End;\r
545 EFI_STATUS Status;\r
546 UINTN Index;\r
547 UINTN FreeIndex;\r
d1102dba 548\r
28a00297 549 if ((Start & EFI_PAGE_MASK) != 0) {\r
550 return;\r
551 }\r
552\r
8ee25f48 553 if (Type >= EfiMaxMemoryType && Type < MEMORY_TYPE_OEM_RESERVED_MIN) {\r
28a00297 554 return;\r
555 }\r
28a00297 556 CoreAcquireMemoryLock ();\r
557 End = Start + LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT) - 1;\r
558 CoreAddRange (Type, Start, End, Attribute);\r
559 CoreFreeMemoryMapStack ();\r
560 CoreReleaseMemoryLock ();\r
561\r
7eb927db 562 ApplyMemoryProtectionPolicy (EfiMaxMemoryType, Type, Start,\r
4879e130 563 LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT));\r
7eb927db 564\r
54ea99a7 565 //\r
566 // If Loading Module At Fixed Address feature is enabled. try to allocate memory with Runtime code & Boot time code type\r
567 //\r
852081fc 568 if (PcdGet64(PcdLoadModuleAtFixAddressEnable) != 0) {\r
54ea99a7 569 CoreLoadingFixedAddressHook();\r
570 }\r
d1102dba 571\r
28a00297 572 //\r
573 // Check to see if the statistics for the different memory types have already been established\r
574 //\r
575 if (mMemoryTypeInformationInitialized) {\r
576 return;\r
577 }\r
578\r
d1102dba 579\r
28a00297 580 //\r
581 // Loop through each memory type in the order specified by the gMemoryTypeInformation[] array\r
582 //\r
583 for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {\r
584 //\r
585 // Make sure the memory type in the gMemoryTypeInformation[] array is valid\r
586 //\r
587 Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[Index].Type);\r
3d78c020 588 if ((UINT32)Type > EfiMaxMemoryType) {\r
28a00297 589 continue;\r
590 }\r
28a00297 591 if (gMemoryTypeInformation[Index].NumberOfPages != 0) {\r
592 //\r
593 // Allocate pages for the current memory type from the top of available memory\r
594 //\r
595 Status = CoreAllocatePages (\r
596 AllocateAnyPages,\r
597 Type,\r
598 gMemoryTypeInformation[Index].NumberOfPages,\r
599 &mMemoryTypeStatistics[Type].BaseAddress\r
600 );\r
601 if (EFI_ERROR (Status)) {\r
602 //\r
022c6d45 603 // If an error occurs allocating the pages for the current memory type, then\r
28a00297 604 // free all the pages allocates for the previous memory types and return. This\r
605 // operation with be retied when/if more memory is added to the system\r
606 //\r
607 for (FreeIndex = 0; FreeIndex < Index; FreeIndex++) {\r
608 //\r
609 // Make sure the memory type in the gMemoryTypeInformation[] array is valid\r
610 //\r
611 Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[FreeIndex].Type);\r
3d78c020 612 if ((UINT32)Type > EfiMaxMemoryType) {\r
28a00297 613 continue;\r
614 }\r
615\r
616 if (gMemoryTypeInformation[FreeIndex].NumberOfPages != 0) {\r
617 CoreFreePages (\r
022c6d45 618 mMemoryTypeStatistics[Type].BaseAddress,\r
28a00297 619 gMemoryTypeInformation[FreeIndex].NumberOfPages\r
620 );\r
621 mMemoryTypeStatistics[Type].BaseAddress = 0;\r
f3f2e05d 622 mMemoryTypeStatistics[Type].MaximumAddress = MAX_ADDRESS;\r
28a00297 623 }\r
624 }\r
625 return;\r
626 }\r
627\r
628 //\r
629 // Compute the address at the top of the current statistics\r
630 //\r
022c6d45 631 mMemoryTypeStatistics[Type].MaximumAddress =\r
632 mMemoryTypeStatistics[Type].BaseAddress +\r
28a00297 633 LShiftU64 (gMemoryTypeInformation[Index].NumberOfPages, EFI_PAGE_SHIFT) - 1;\r
634\r
635 //\r
022c6d45 636 // If the current base address is the lowest address so far, then update the default\r
28a00297 637 // maximum address\r
638 //\r
639 if (mMemoryTypeStatistics[Type].BaseAddress < mDefaultMaximumAddress) {\r
640 mDefaultMaximumAddress = mMemoryTypeStatistics[Type].BaseAddress - 1;\r
641 }\r
642 }\r
643 }\r
644\r
645 //\r
646 // There was enough system memory for all the the memory types were allocated. So,\r
647 // those memory areas can be freed for future allocations, and all future memory\r
648 // allocations can occur within their respective bins\r
649 //\r
650 for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {\r
651 //\r
652 // Make sure the memory type in the gMemoryTypeInformation[] array is valid\r
653 //\r
654 Type = (EFI_MEMORY_TYPE) (gMemoryTypeInformation[Index].Type);\r
3d78c020 655 if ((UINT32)Type > EfiMaxMemoryType) {\r
28a00297 656 continue;\r
657 }\r
28a00297 658 if (gMemoryTypeInformation[Index].NumberOfPages != 0) {\r
659 CoreFreePages (\r
022c6d45 660 mMemoryTypeStatistics[Type].BaseAddress,\r
28a00297 661 gMemoryTypeInformation[Index].NumberOfPages\r
662 );\r
b74350e9 663 mMemoryTypeStatistics[Type].NumberOfPages = gMemoryTypeInformation[Index].NumberOfPages;\r
28a00297 664 gMemoryTypeInformation[Index].NumberOfPages = 0;\r
665 }\r
666 }\r
667\r
668 //\r
669 // If the number of pages reserved for a memory type is 0, then all allocations for that type\r
670 // should be in the default range.\r
671 //\r
672 for (Type = (EFI_MEMORY_TYPE) 0; Type < EfiMaxMemoryType; Type++) {\r
673 for (Index = 0; gMemoryTypeInformation[Index].Type != EfiMaxMemoryType; Index++) {\r
674 if (Type == (EFI_MEMORY_TYPE)gMemoryTypeInformation[Index].Type) {\r
675 mMemoryTypeStatistics[Type].InformationIndex = Index;\r
676 }\r
677 }\r
678 mMemoryTypeStatistics[Type].CurrentNumberOfPages = 0;\r
f3f2e05d 679 if (mMemoryTypeStatistics[Type].MaximumAddress == MAX_ADDRESS) {\r
28a00297 680 mMemoryTypeStatistics[Type].MaximumAddress = mDefaultMaximumAddress;\r
681 }\r
682 }\r
683\r
684 mMemoryTypeInformationInitialized = TRUE;\r
685}\r
686\r
687\r
162ed594 688/**\r
771ee501
EC
689 Internal function. Converts a memory range to the specified type or attributes.\r
690 The range must exist in the memory map. Either ChangingType or\r
691 ChangingAttributes must be set, but not both.\r
162ed594 692\r
022c6d45 693 @param Start The first address of the range Must be page\r
694 aligned\r
695 @param NumberOfPages The number of pages to convert\r
771ee501 696 @param ChangingType Boolean indicating that type value should be changed\r
022c6d45 697 @param NewType The new type for the memory range\r
771ee501
EC
698 @param ChangingAttributes Boolean indicating that attributes value should be changed\r
699 @param NewAttributes The new attributes for the memory range\r
162ed594 700\r
022c6d45 701 @retval EFI_INVALID_PARAMETER Invalid parameter\r
702 @retval EFI_NOT_FOUND Could not find a descriptor cover the specified\r
703 range or convertion not allowed.\r
704 @retval EFI_SUCCESS Successfully converts the memory range to the\r
162ed594 705 specified type.\r
706\r
707**/\r
28a00297 708EFI_STATUS\r
771ee501 709CoreConvertPagesEx (\r
28a00297 710 IN UINT64 Start,\r
711 IN UINT64 NumberOfPages,\r
771ee501
EC
712 IN BOOLEAN ChangingType,\r
713 IN EFI_MEMORY_TYPE NewType,\r
714 IN BOOLEAN ChangingAttributes,\r
715 IN UINT64 NewAttributes\r
28a00297 716 )\r
28a00297 717{\r
718\r
719 UINT64 NumberOfBytes;\r
720 UINT64 End;\r
721 UINT64 RangeEnd;\r
722 UINT64 Attribute;\r
771ee501 723 EFI_MEMORY_TYPE MemType;\r
28a00297 724 LIST_ENTRY *Link;\r
725 MEMORY_MAP *Entry;\r
726\r
727 Entry = NULL;\r
728 NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);\r
729 End = Start + NumberOfBytes - 1;\r
730\r
731 ASSERT (NumberOfPages);\r
732 ASSERT ((Start & EFI_PAGE_MASK) == 0);\r
733 ASSERT (End > Start) ;\r
734 ASSERT_LOCKED (&gMemoryLock);\r
771ee501 735 ASSERT ( (ChangingType == FALSE) || (ChangingAttributes == FALSE) );\r
28a00297 736\r
f0459afe 737 if (NumberOfPages == 0 || ((Start & EFI_PAGE_MASK) != 0) || (Start >= End)) {\r
28a00297 738 return EFI_INVALID_PARAMETER;\r
739 }\r
740\r
741 //\r
742 // Convert the entire range\r
743 //\r
744\r
745 while (Start < End) {\r
746\r
747 //\r
748 // Find the entry that the covers the range\r
749 //\r
750 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
751 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
752\r
753 if (Entry->Start <= Start && Entry->End > Start) {\r
754 break;\r
755 }\r
756 }\r
757\r
758 if (Link == &gMemoryMap) {\r
162ed594 759 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: failed to find range %lx - %lx\n", Start, End));\r
28a00297 760 return EFI_NOT_FOUND;\r
761 }\r
762\r
3f2ae009
AB
763 //\r
764 // If we are converting the type of the range from EfiConventionalMemory to\r
765 // another type, we have to ensure that the entire range is covered by a\r
766 // single entry.\r
767 //\r
768 if (ChangingType && (NewType != EfiConventionalMemory)) {\r
769 if (Entry->End < End) {\r
770 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: range %lx - %lx covers multiple entries\n", Start, End));\r
771 return EFI_NOT_FOUND;\r
772 }\r
773 }\r
28a00297 774 //\r
775 // Convert range to the end, or to the end of the descriptor\r
776 // if that's all we've got\r
777 //\r
778 RangeEnd = End;\r
525aded9 779\r
780 ASSERT (Entry != NULL);\r
28a00297 781 if (Entry->End < End) {\r
782 RangeEnd = Entry->End;\r
783 }\r
784\r
771ee501
EC
785 if (ChangingType) {\r
786 DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to type %d\n", Start, RangeEnd, NewType));\r
787 }\r
788 if (ChangingAttributes) {\r
789 DEBUG ((DEBUG_PAGE, "ConvertRange: %lx-%lx to attr %lx\n", Start, RangeEnd, NewAttributes));\r
022c6d45 790 }\r
28a00297 791\r
771ee501
EC
792 if (ChangingType) {\r
793 //\r
794 // Debug code - verify conversion is allowed\r
795 //\r
796 if (!(NewType == EfiConventionalMemory ? 1 : 0) ^ (Entry->Type == EfiConventionalMemory ? 1 : 0)) {\r
9a701955
SZ
797 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "ConvertPages: Incompatible memory types, "));\r
798 if (Entry->Type == EfiConventionalMemory) {\r
799 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "the pages to free have been freed\n"));\r
800 } else {\r
801 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "the pages to allocate have been allocated\n"));\r
802 }\r
771ee501
EC
803 return EFI_NOT_FOUND;\r
804 }\r
805\r
806 //\r
807 // Update counters for the number of pages allocated to each memory type\r
808 //\r
809 if ((UINT32)Entry->Type < EfiMaxMemoryType) {\r
810 if ((Start >= mMemoryTypeStatistics[Entry->Type].BaseAddress && Start <= mMemoryTypeStatistics[Entry->Type].MaximumAddress) ||\r
811 (Start >= mDefaultBaseAddress && Start <= mDefaultMaximumAddress) ) {\r
812 if (NumberOfPages > mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages) {\r
813 mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages = 0;\r
814 } else {\r
815 mMemoryTypeStatistics[Entry->Type].CurrentNumberOfPages -= NumberOfPages;\r
816 }\r
28a00297 817 }\r
818 }\r
28a00297 819\r
771ee501
EC
820 if ((UINT32)NewType < EfiMaxMemoryType) {\r
821 if ((Start >= mMemoryTypeStatistics[NewType].BaseAddress && Start <= mMemoryTypeStatistics[NewType].MaximumAddress) ||\r
822 (Start >= mDefaultBaseAddress && Start <= mDefaultMaximumAddress) ) {\r
823 mMemoryTypeStatistics[NewType].CurrentNumberOfPages += NumberOfPages;\r
824 if (mMemoryTypeStatistics[NewType].CurrentNumberOfPages > gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages) {\r
825 gMemoryTypeInformation[mMemoryTypeStatistics[NewType].InformationIndex].NumberOfPages = (UINT32)mMemoryTypeStatistics[NewType].CurrentNumberOfPages;\r
826 }\r
28a00297 827 }\r
828 }\r
829 }\r
830\r
831 //\r
832 // Pull range out of descriptor\r
833 //\r
834 if (Entry->Start == Start) {\r
022c6d45 835\r
28a00297 836 //\r
837 // Clip start\r
838 //\r
839 Entry->Start = RangeEnd + 1;\r
840\r
841 } else if (Entry->End == RangeEnd) {\r
022c6d45 842\r
28a00297 843 //\r
844 // Clip end\r
845 //\r
846 Entry->End = Start - 1;\r
847\r
848 } else {\r
849\r
850 //\r
851 // Pull it out of the center, clip current\r
852 //\r
022c6d45 853\r
28a00297 854 //\r
855 // Add a new one\r
856 //\r
857 mMapStack[mMapDepth].Signature = MEMORY_MAP_SIGNATURE;\r
858 mMapStack[mMapDepth].FromPages = FALSE;\r
859 mMapStack[mMapDepth].Type = Entry->Type;\r
860 mMapStack[mMapDepth].Start = RangeEnd+1;\r
861 mMapStack[mMapDepth].End = Entry->End;\r
862\r
863 //\r
864 // Inherit Attribute from the Memory Descriptor that is being clipped\r
865 //\r
866 mMapStack[mMapDepth].Attribute = Entry->Attribute;\r
867\r
868 Entry->End = Start - 1;\r
869 ASSERT (Entry->Start < Entry->End);\r
870\r
871 Entry = &mMapStack[mMapDepth];\r
872 InsertTailList (&gMemoryMap, &Entry->Link);\r
873\r
874 mMapDepth += 1;\r
875 ASSERT (mMapDepth < MAX_MAP_DEPTH);\r
876 }\r
877\r
878 //\r
022c6d45 879 // The new range inherits the same Attribute as the Entry\r
771ee501 880 // it is being cut out of unless attributes are being changed\r
28a00297 881 //\r
771ee501
EC
882 if (ChangingType) {\r
883 Attribute = Entry->Attribute;\r
884 MemType = NewType;\r
885 } else {\r
886 Attribute = NewAttributes;\r
887 MemType = Entry->Type;\r
888 }\r
28a00297 889\r
890 //\r
891 // If the descriptor is empty, then remove it from the map\r
892 //\r
893 if (Entry->Start == Entry->End + 1) {\r
894 RemoveMemoryMapEntry (Entry);\r
895 Entry = NULL;\r
896 }\r
022c6d45 897\r
28a00297 898 //\r
899 // Add our new range in\r
900 //\r
771ee501
EC
901 CoreAddRange (MemType, Start, RangeEnd, Attribute);\r
902 if (ChangingType && (MemType == EfiConventionalMemory)) {\r
425d2569
JW
903 //\r
904 // Avoid calling DEBUG_CLEAR_MEMORY() for an address of 0 because this\r
905 // macro will ASSERT() if address is 0. Instead, CoreAddRange() guarantees\r
906 // that the page starting at address 0 is always filled with zeros.\r
907 //\r
9a340872 908 if (Start == 0) {\r
909 if (RangeEnd > EFI_PAGE_SIZE) {\r
910 DEBUG_CLEAR_MEMORY ((VOID *)(UINTN) EFI_PAGE_SIZE, (UINTN) (RangeEnd - EFI_PAGE_SIZE + 1));\r
911 }\r
912 } else {\r
425d2569 913 DEBUG_CLEAR_MEMORY ((VOID *)(UINTN) Start, (UINTN) (RangeEnd - Start + 1));\r
9a340872 914 }\r
38c7df98 915 }\r
28a00297 916\r
917 //\r
918 // Move any map descriptor stack to general pool\r
919 //\r
920 CoreFreeMemoryMapStack ();\r
921\r
922 //\r
923 // Bump the starting address, and convert the next range\r
924 //\r
925 Start = RangeEnd + 1;\r
926 }\r
927\r
928 //\r
929 // Converted the whole range, done\r
930 //\r
931\r
932 return EFI_SUCCESS;\r
933}\r
934\r
935\r
771ee501
EC
936/**\r
937 Internal function. Converts a memory range to the specified type.\r
938 The range must exist in the memory map.\r
939\r
940 @param Start The first address of the range Must be page\r
941 aligned\r
942 @param NumberOfPages The number of pages to convert\r
943 @param NewType The new type for the memory range\r
944\r
945 @retval EFI_INVALID_PARAMETER Invalid parameter\r
946 @retval EFI_NOT_FOUND Could not find a descriptor cover the specified\r
947 range or convertion not allowed.\r
948 @retval EFI_SUCCESS Successfully converts the memory range to the\r
949 specified type.\r
950\r
951**/\r
952EFI_STATUS\r
953CoreConvertPages (\r
954 IN UINT64 Start,\r
955 IN UINT64 NumberOfPages,\r
956 IN EFI_MEMORY_TYPE NewType\r
957 )\r
958{\r
959 return CoreConvertPagesEx(Start, NumberOfPages, TRUE, NewType, FALSE, 0);\r
960}\r
961\r
962\r
963/**\r
964 Internal function. Converts a memory range to use new attributes.\r
965\r
966 @param Start The first address of the range Must be page\r
967 aligned\r
968 @param NumberOfPages The number of pages to convert\r
969 @param NewAttributes The new attributes value for the range.\r
970\r
771ee501
EC
971**/\r
972VOID\r
973CoreUpdateMemoryAttributes (\r
974 IN EFI_PHYSICAL_ADDRESS Start,\r
975 IN UINT64 NumberOfPages,\r
976 IN UINT64 NewAttributes\r
977 )\r
978{\r
979 CoreAcquireMemoryLock ();\r
980\r
981 //\r
982 // Update the attributes to the new value\r
983 //\r
984 CoreConvertPagesEx(Start, NumberOfPages, FALSE, (EFI_MEMORY_TYPE)0, TRUE, NewAttributes);\r
985\r
986 CoreReleaseMemoryLock ();\r
987}\r
988\r
162ed594 989\r
990/**\r
991 Internal function. Finds a consecutive free page range below\r
992 the requested address.\r
993\r
022c6d45 994 @param MaxAddress The address that the range must be below\r
35f9e94e 995 @param MinAddress The address that the range must be above\r
022c6d45 996 @param NumberOfPages Number of pages needed\r
997 @param NewType The type of memory the range is going to be\r
998 turned into\r
999 @param Alignment Bits to align with\r
e63da9f0 1000 @param NeedGuard Flag to indicate Guard page is needed or not\r
162ed594 1001\r
1002 @return The base address of the range, or 0 if the range was not found\r
1003\r
1004**/\r
28a00297 1005UINT64\r
1006CoreFindFreePagesI (\r
1007 IN UINT64 MaxAddress,\r
2345e7d4 1008 IN UINT64 MinAddress,\r
28a00297 1009 IN UINT64 NumberOfPages,\r
1010 IN EFI_MEMORY_TYPE NewType,\r
e63da9f0
JW
1011 IN UINTN Alignment,\r
1012 IN BOOLEAN NeedGuard\r
28a00297 1013 )\r
28a00297 1014{\r
1015 UINT64 NumberOfBytes;\r
1016 UINT64 Target;\r
1017 UINT64 DescStart;\r
1018 UINT64 DescEnd;\r
1019 UINT64 DescNumberOfBytes;\r
1020 LIST_ENTRY *Link;\r
1021 MEMORY_MAP *Entry;\r
1022\r
1023 if ((MaxAddress < EFI_PAGE_MASK) ||(NumberOfPages == 0)) {\r
1024 return 0;\r
1025 }\r
1026\r
1027 if ((MaxAddress & EFI_PAGE_MASK) != EFI_PAGE_MASK) {\r
022c6d45 1028\r
28a00297 1029 //\r
1030 // If MaxAddress is not aligned to the end of a page\r
1031 //\r
022c6d45 1032\r
28a00297 1033 //\r
1034 // Change MaxAddress to be 1 page lower\r
1035 //\r
1036 MaxAddress -= (EFI_PAGE_MASK + 1);\r
022c6d45 1037\r
28a00297 1038 //\r
1039 // Set MaxAddress to a page boundary\r
1040 //\r
6e1e5405 1041 MaxAddress &= ~(UINT64)EFI_PAGE_MASK;\r
022c6d45 1042\r
28a00297 1043 //\r
1044 // Set MaxAddress to end of the page\r
1045 //\r
1046 MaxAddress |= EFI_PAGE_MASK;\r
1047 }\r
1048\r
1049 NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);\r
1050 Target = 0;\r
1051\r
1052 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
1053 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
022c6d45 1054\r
28a00297 1055 //\r
1056 // If it's not a free entry, don't bother with it\r
1057 //\r
1058 if (Entry->Type != EfiConventionalMemory) {\r
1059 continue;\r
1060 }\r
1061\r
1062 DescStart = Entry->Start;\r
1063 DescEnd = Entry->End;\r
1064\r
1065 //\r
2345e7d4 1066 // If desc is past max allowed address or below min allowed address, skip it\r
28a00297 1067 //\r
2345e7d4 1068 if ((DescStart >= MaxAddress) || (DescEnd < MinAddress)) {\r
28a00297 1069 continue;\r
1070 }\r
1071\r
1072 //\r
1073 // If desc ends past max allowed address, clip the end\r
1074 //\r
1075 if (DescEnd >= MaxAddress) {\r
1076 DescEnd = MaxAddress;\r
1077 }\r
1078\r
1079 DescEnd = ((DescEnd + 1) & (~(Alignment - 1))) - 1;\r
1080\r
1365bedd
HG
1081 // Skip if DescEnd is less than DescStart after alignment clipping\r
1082 if (DescEnd < DescStart) {\r
1083 continue;\r
1084 }\r
1085\r
28a00297 1086 //\r
022c6d45 1087 // Compute the number of bytes we can used from this\r
28a00297 1088 // descriptor, and see it's enough to satisfy the request\r
1089 //\r
1090 DescNumberOfBytes = DescEnd - DescStart + 1;\r
1091\r
1092 if (DescNumberOfBytes >= NumberOfBytes) {\r
2345e7d4 1093 //\r
1094 // If the start of the allocated range is below the min address allowed, skip it\r
1095 //\r
1096 if ((DescEnd - NumberOfBytes + 1) < MinAddress) {\r
1097 continue;\r
1098 }\r
28a00297 1099\r
1100 //\r
1101 // If this is the best match so far remember it\r
1102 //\r
1103 if (DescEnd > Target) {\r
e63da9f0
JW
1104 if (NeedGuard) {\r
1105 DescEnd = AdjustMemoryS (\r
1106 DescEnd + 1 - DescNumberOfBytes,\r
1107 DescNumberOfBytes,\r
1108 NumberOfBytes\r
1109 );\r
1110 if (DescEnd == 0) {\r
1111 continue;\r
1112 }\r
1113 }\r
1114\r
28a00297 1115 Target = DescEnd;\r
1116 }\r
1117 }\r
022c6d45 1118 }\r
28a00297 1119\r
1120 //\r
1121 // If this is a grow down, adjust target to be the allocation base\r
1122 //\r
1123 Target -= NumberOfBytes - 1;\r
1124\r
1125 //\r
1126 // If we didn't find a match, return 0\r
1127 //\r
1128 if ((Target & EFI_PAGE_MASK) != 0) {\r
1129 return 0;\r
1130 }\r
1131\r
1132 return Target;\r
1133}\r
1134\r
162ed594 1135\r
1136/**\r
1137 Internal function. Finds a consecutive free page range below\r
1138 the requested address\r
1139\r
022c6d45 1140 @param MaxAddress The address that the range must be below\r
1141 @param NoPages Number of pages needed\r
1142 @param NewType The type of memory the range is going to be\r
1143 turned into\r
1144 @param Alignment Bits to align with\r
e63da9f0 1145 @param NeedGuard Flag to indicate Guard page is needed or not\r
162ed594 1146\r
1147 @return The base address of the range, or 0 if the range was not found.\r
1148\r
1149**/\r
28a00297 1150UINT64\r
1151FindFreePages (\r
1152 IN UINT64 MaxAddress,\r
1153 IN UINT64 NoPages,\r
1154 IN EFI_MEMORY_TYPE NewType,\r
e63da9f0
JW
1155 IN UINTN Alignment,\r
1156 IN BOOLEAN NeedGuard\r
28a00297 1157 )\r
28a00297 1158{\r
2345e7d4 1159 UINT64 Start;\r
28a00297 1160\r
2345e7d4 1161 //\r
1162 // Attempt to find free pages in the preferred bin based on the requested memory type\r
1163 //\r
3d78c020 1164 if ((UINT32)NewType < EfiMaxMemoryType && MaxAddress >= mMemoryTypeStatistics[NewType].MaximumAddress) {\r
2345e7d4 1165 Start = CoreFindFreePagesI (\r
d1102dba
LG
1166 mMemoryTypeStatistics[NewType].MaximumAddress,\r
1167 mMemoryTypeStatistics[NewType].BaseAddress,\r
1168 NoPages,\r
1169 NewType,\r
e63da9f0
JW
1170 Alignment,\r
1171 NeedGuard\r
2345e7d4 1172 );\r
1173 if (Start != 0) {\r
1174 return Start;\r
1175 }\r
1176 }\r
28a00297 1177\r
2345e7d4 1178 //\r
1179 // Attempt to find free pages in the default allocation bin\r
1180 //\r
1181 if (MaxAddress >= mDefaultMaximumAddress) {\r
e63da9f0
JW
1182 Start = CoreFindFreePagesI (mDefaultMaximumAddress, 0, NoPages, NewType,\r
1183 Alignment, NeedGuard);\r
2345e7d4 1184 if (Start != 0) {\r
1185 if (Start < mDefaultBaseAddress) {\r
1186 mDefaultBaseAddress = Start;\r
1187 }\r
1188 return Start;\r
28a00297 1189 }\r
1190 }\r
1191\r
2345e7d4 1192 //\r
d1102dba
LG
1193 // The allocation did not succeed in any of the prefered bins even after\r
1194 // promoting resources. Attempt to find free pages anywhere is the requested\r
1195 // address range. If this allocation fails, then there are not enough\r
2345e7d4 1196 // resources anywhere to satisfy the request.\r
1197 //\r
e63da9f0
JW
1198 Start = CoreFindFreePagesI (MaxAddress, 0, NoPages, NewType, Alignment,\r
1199 NeedGuard);\r
2345e7d4 1200 if (Start != 0) {\r
1201 return Start;\r
1202 }\r
28a00297 1203\r
2345e7d4 1204 //\r
1205 // If allocations from the preferred bins fail, then attempt to promote memory resources.\r
1206 //\r
1207 if (!PromoteMemoryResource ()) {\r
1208 return 0;\r
28a00297 1209 }\r
1210\r
2345e7d4 1211 //\r
1212 // If any memory resources were promoted, then re-attempt the allocation\r
1213 //\r
e63da9f0 1214 return FindFreePages (MaxAddress, NoPages, NewType, Alignment, NeedGuard);\r
28a00297 1215}\r
1216\r
1217\r
162ed594 1218/**\r
1219 Allocates pages from the memory map.\r
1220\r
022c6d45 1221 @param Type The type of allocation to perform\r
1222 @param MemoryType The type of memory to turn the allocated pages\r
1223 into\r
1224 @param NumberOfPages The number of pages to allocate\r
1225 @param Memory A pointer to receive the base allocated memory\r
1226 address\r
e63da9f0 1227 @param NeedGuard Flag to indicate Guard page is needed or not\r
162ed594 1228\r
1229 @return Status. On success, Memory is filled in with the base address allocated\r
022c6d45 1230 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in\r
1231 spec.\r
1232 @retval EFI_NOT_FOUND Could not allocate pages match the requirement.\r
1233 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.\r
162ed594 1234 @retval EFI_SUCCESS Pages successfully allocated.\r
1235\r
1236**/\r
28a00297 1237EFI_STATUS\r
1238EFIAPI\r
84edd20b 1239CoreInternalAllocatePages (\r
28a00297 1240 IN EFI_ALLOCATE_TYPE Type,\r
1241 IN EFI_MEMORY_TYPE MemoryType,\r
1242 IN UINTN NumberOfPages,\r
e63da9f0
JW
1243 IN OUT EFI_PHYSICAL_ADDRESS *Memory,\r
1244 IN BOOLEAN NeedGuard\r
28a00297 1245 )\r
28a00297 1246{\r
1247 EFI_STATUS Status;\r
1248 UINT64 Start;\r
c2a07a10
SZ
1249 UINT64 NumberOfBytes;\r
1250 UINT64 End;\r
28a00297 1251 UINT64 MaxAddress;\r
1252 UINTN Alignment;\r
1253\r
3d78c020 1254 if ((UINT32)Type >= MaxAllocateType) {\r
28a00297 1255 return EFI_INVALID_PARAMETER;\r
1256 }\r
1257\r
8ee25f48 1258 if ((MemoryType >= EfiMaxMemoryType && MemoryType < MEMORY_TYPE_OEM_RESERVED_MIN) ||\r
a671a012 1259 (MemoryType == EfiConventionalMemory) || (MemoryType == EfiPersistentMemory)) {\r
28a00297 1260 return EFI_INVALID_PARAMETER;\r
1261 }\r
1262\r
3e058701
ED
1263 if (Memory == NULL) {\r
1264 return EFI_INVALID_PARAMETER;\r
1265 }\r
1266\r
d4731a98 1267 Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY;\r
28a00297 1268\r
1269 if (MemoryType == EfiACPIReclaimMemory ||\r
1270 MemoryType == EfiACPIMemoryNVS ||\r
1271 MemoryType == EfiRuntimeServicesCode ||\r
1272 MemoryType == EfiRuntimeServicesData) {\r
1273\r
d4731a98 1274 Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;\r
28a00297 1275 }\r
1276\r
1277 if (Type == AllocateAddress) {\r
1278 if ((*Memory & (Alignment - 1)) != 0) {\r
1279 return EFI_NOT_FOUND;\r
1280 }\r
1281 }\r
1282\r
1283 NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;\r
1284 NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);\r
1285\r
1286 //\r
022c6d45 1287 // If this is for below a particular address, then\r
28a00297 1288 //\r
1289 Start = *Memory;\r
022c6d45 1290\r
28a00297 1291 //\r
1292 // The max address is the max natively addressable address for the processor\r
1293 //\r
f3f2e05d 1294 MaxAddress = MAX_ADDRESS;\r
022c6d45 1295\r
c2a07a10
SZ
1296 //\r
1297 // Check for Type AllocateAddress,\r
1298 // if NumberOfPages is 0 or\r
1299 // if (NumberOfPages << EFI_PAGE_SHIFT) is above MAX_ADDRESS or\r
1300 // if (Start + NumberOfBytes) rolls over 0 or\r
1301 // if Start is above MAX_ADDRESS or\r
1302 // if End is above MAX_ADDRESS,\r
1303 // return EFI_NOT_FOUND.\r
1304 //\r
1305 if (Type == AllocateAddress) {\r
1306 if ((NumberOfPages == 0) ||\r
1307 (NumberOfPages > RShiftU64 (MaxAddress, EFI_PAGE_SHIFT))) {\r
1308 return EFI_NOT_FOUND;\r
1309 }\r
1310 NumberOfBytes = LShiftU64 (NumberOfPages, EFI_PAGE_SHIFT);\r
1311 End = Start + NumberOfBytes - 1;\r
1312\r
1313 if ((Start >= End) ||\r
d1102dba 1314 (Start > MaxAddress) ||\r
c2a07a10
SZ
1315 (End > MaxAddress)) {\r
1316 return EFI_NOT_FOUND;\r
1317 }\r
1318 }\r
1319\r
28a00297 1320 if (Type == AllocateMaxAddress) {\r
1321 MaxAddress = Start;\r
1322 }\r
1323\r
1324 CoreAcquireMemoryLock ();\r
022c6d45 1325\r
28a00297 1326 //\r
1327 // If not a specific address, then find an address to allocate\r
1328 //\r
1329 if (Type != AllocateAddress) {\r
e63da9f0
JW
1330 Start = FindFreePages (MaxAddress, NumberOfPages, MemoryType, Alignment,\r
1331 NeedGuard);\r
28a00297 1332 if (Start == 0) {\r
1333 Status = EFI_OUT_OF_RESOURCES;\r
1334 goto Done;\r
1335 }\r
1336 }\r
1337\r
1338 //\r
1339 // Convert pages from FreeMemory to the requested type\r
1340 //\r
e63da9f0
JW
1341 if (NeedGuard) {\r
1342 Status = CoreConvertPagesWithGuard(Start, NumberOfPages, MemoryType);\r
1343 } else {\r
1344 Status = CoreConvertPages(Start, NumberOfPages, MemoryType);\r
1345 }\r
28a00297 1346\r
1347Done:\r
1348 CoreReleaseMemoryLock ();\r
1349\r
1350 if (!EFI_ERROR (Status)) {\r
e63da9f0
JW
1351 if (NeedGuard) {\r
1352 SetGuardForMemory (Start, NumberOfPages);\r
1353 }\r
28a00297 1354 *Memory = Start;\r
1355 }\r
1356\r
1357 return Status;\r
1358}\r
1359\r
84edd20b
SZ
1360/**\r
1361 Allocates pages from the memory map.\r
1362\r
1363 @param Type The type of allocation to perform\r
1364 @param MemoryType The type of memory to turn the allocated pages\r
1365 into\r
1366 @param NumberOfPages The number of pages to allocate\r
1367 @param Memory A pointer to receive the base allocated memory\r
1368 address\r
1369\r
1370 @return Status. On success, Memory is filled in with the base address allocated\r
1371 @retval EFI_INVALID_PARAMETER Parameters violate checking rules defined in\r
1372 spec.\r
1373 @retval EFI_NOT_FOUND Could not allocate pages match the requirement.\r
1374 @retval EFI_OUT_OF_RESOURCES No enough pages to allocate.\r
1375 @retval EFI_SUCCESS Pages successfully allocated.\r
1376\r
1377**/\r
1378EFI_STATUS\r
1379EFIAPI\r
1380CoreAllocatePages (\r
1381 IN EFI_ALLOCATE_TYPE Type,\r
1382 IN EFI_MEMORY_TYPE MemoryType,\r
1383 IN UINTN NumberOfPages,\r
1384 OUT EFI_PHYSICAL_ADDRESS *Memory\r
1385 )\r
1386{\r
1387 EFI_STATUS Status;\r
e63da9f0 1388 BOOLEAN NeedGuard;\r
84edd20b 1389\r
e63da9f0
JW
1390 NeedGuard = IsPageTypeToGuard (MemoryType, Type) && !mOnGuarding;\r
1391 Status = CoreInternalAllocatePages (Type, MemoryType, NumberOfPages, Memory,\r
1392 NeedGuard);\r
84edd20b 1393 if (!EFI_ERROR (Status)) {\r
1d60fe96
SZ
1394 CoreUpdateProfile (\r
1395 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),\r
1396 MemoryProfileActionAllocatePages,\r
1397 MemoryType,\r
1398 EFI_PAGES_TO_SIZE (NumberOfPages),\r
1399 (VOID *) (UINTN) *Memory,\r
1400 NULL\r
1401 );\r
74a88770 1402 InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);\r
7eb927db
AB
1403 ApplyMemoryProtectionPolicy (EfiConventionalMemory, MemoryType, *Memory,\r
1404 EFI_PAGES_TO_SIZE (NumberOfPages));\r
84edd20b
SZ
1405 }\r
1406 return Status;\r
1407}\r
28a00297 1408\r
162ed594 1409/**\r
1410 Frees previous allocated pages.\r
1411\r
022c6d45 1412 @param Memory Base address of memory being freed\r
1413 @param NumberOfPages The number of pages to free\r
925f0d1a 1414 @param MemoryType Pointer to memory type\r
162ed594 1415\r
022c6d45 1416 @retval EFI_NOT_FOUND Could not find the entry that covers the range\r
1417 @retval EFI_INVALID_PARAMETER Address not aligned\r
162ed594 1418 @return EFI_SUCCESS -Pages successfully freed.\r
1419\r
1420**/\r
022c6d45 1421EFI_STATUS\r
28a00297 1422EFIAPI\r
84edd20b 1423CoreInternalFreePages (\r
28a00297 1424 IN EFI_PHYSICAL_ADDRESS Memory,\r
925f0d1a
SZ
1425 IN UINTN NumberOfPages,\r
1426 OUT EFI_MEMORY_TYPE *MemoryType OPTIONAL\r
28a00297 1427 )\r
28a00297 1428{\r
1429 EFI_STATUS Status;\r
1430 LIST_ENTRY *Link;\r
1431 MEMORY_MAP *Entry;\r
1432 UINTN Alignment;\r
e63da9f0 1433 BOOLEAN IsGuarded;\r
28a00297 1434\r
1435 //\r
1436 // Free the range\r
1437 //\r
1438 CoreAcquireMemoryLock ();\r
1439\r
1440 //\r
1441 // Find the entry that the covers the range\r
1442 //\r
e63da9f0 1443 IsGuarded = FALSE;\r
28a00297 1444 Entry = NULL;\r
1445 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
1446 Entry = CR(Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
1447 if (Entry->Start <= Memory && Entry->End > Memory) {\r
1448 break;\r
1449 }\r
1450 }\r
1451 if (Link == &gMemoryMap) {\r
a5ca8fa7 1452 Status = EFI_NOT_FOUND;\r
1453 goto Done;\r
28a00297 1454 }\r
1455\r
d4731a98 1456 Alignment = DEFAULT_PAGE_ALLOCATION_GRANULARITY;\r
28a00297 1457\r
525aded9 1458 ASSERT (Entry != NULL);\r
28a00297 1459 if (Entry->Type == EfiACPIReclaimMemory ||\r
1460 Entry->Type == EfiACPIMemoryNVS ||\r
1461 Entry->Type == EfiRuntimeServicesCode ||\r
1462 Entry->Type == EfiRuntimeServicesData) {\r
1463\r
d4731a98 1464 Alignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY;\r
28a00297 1465\r
1466 }\r
1467\r
1468 if ((Memory & (Alignment - 1)) != 0) {\r
a5ca8fa7 1469 Status = EFI_INVALID_PARAMETER;\r
1470 goto Done;\r
28a00297 1471 }\r
1472\r
1473 NumberOfPages += EFI_SIZE_TO_PAGES (Alignment) - 1;\r
1474 NumberOfPages &= ~(EFI_SIZE_TO_PAGES (Alignment) - 1);\r
1475\r
925f0d1a
SZ
1476 if (MemoryType != NULL) {\r
1477 *MemoryType = Entry->Type;\r
1478 }\r
1479\r
e63da9f0
JW
1480 IsGuarded = IsPageTypeToGuard (Entry->Type, AllocateAnyPages) &&\r
1481 IsMemoryGuarded (Memory);\r
1482 if (IsGuarded) {\r
1483 Status = CoreConvertPagesWithGuard (Memory, NumberOfPages,\r
1484 EfiConventionalMemory);\r
1485 } else {\r
1486 Status = CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);\r
28a00297 1487 }\r
1488\r
a5ca8fa7 1489Done:\r
1490 CoreReleaseMemoryLock ();\r
28a00297 1491 return Status;\r
1492}\r
1493\r
84edd20b
SZ
1494/**\r
1495 Frees previous allocated pages.\r
1496\r
1497 @param Memory Base address of memory being freed\r
1498 @param NumberOfPages The number of pages to free\r
1499\r
1500 @retval EFI_NOT_FOUND Could not find the entry that covers the range\r
1501 @retval EFI_INVALID_PARAMETER Address not aligned\r
1502 @return EFI_SUCCESS -Pages successfully freed.\r
1503\r
1504**/\r
1505EFI_STATUS\r
1506EFIAPI\r
1507CoreFreePages (\r
1508 IN EFI_PHYSICAL_ADDRESS Memory,\r
1509 IN UINTN NumberOfPages\r
1510 )\r
1511{\r
925f0d1a
SZ
1512 EFI_STATUS Status;\r
1513 EFI_MEMORY_TYPE MemoryType;\r
736a692e 1514\r
925f0d1a 1515 Status = CoreInternalFreePages (Memory, NumberOfPages, &MemoryType);\r
736a692e 1516 if (!EFI_ERROR (Status)) {\r
1d60fe96
SZ
1517 CoreUpdateProfile (\r
1518 (EFI_PHYSICAL_ADDRESS) (UINTN) RETURN_ADDRESS (0),\r
1519 MemoryProfileActionFreePages,\r
1520 MemoryType,\r
1521 EFI_PAGES_TO_SIZE (NumberOfPages),\r
1522 (VOID *) (UINTN) Memory,\r
1523 NULL\r
1524 );\r
74a88770 1525 InstallMemoryAttributesTableOnMemoryAllocation (MemoryType);\r
7eb927db
AB
1526 ApplyMemoryProtectionPolicy (MemoryType, EfiConventionalMemory, Memory,\r
1527 EFI_PAGES_TO_SIZE (NumberOfPages));\r
736a692e
HT
1528 }\r
1529 return Status;\r
1530}\r
84edd20b 1531\r
2345e7d4 1532/**\r
1533 This function checks to see if the last memory map descriptor in a memory map\r
1534 can be merged with any of the other memory map descriptors in a memorymap.\r
1535 Memory descriptors may be merged if they are adjacent and have the same type\r
1536 and attributes.\r
1537\r
1538 @param MemoryMap A pointer to the start of the memory map.\r
1539 @param MemoryMapDescriptor A pointer to the last descriptor in MemoryMap.\r
1540 @param DescriptorSize The size, in bytes, of an individual\r
1541 EFI_MEMORY_DESCRIPTOR.\r
1542\r
1543 @return A pointer to the next available descriptor in MemoryMap\r
1544\r
1545**/\r
1546EFI_MEMORY_DESCRIPTOR *\r
1547MergeMemoryMapDescriptor (\r
1548 IN EFI_MEMORY_DESCRIPTOR *MemoryMap,\r
1549 IN EFI_MEMORY_DESCRIPTOR *MemoryMapDescriptor,\r
1550 IN UINTN DescriptorSize\r
1551 )\r
1552{\r
1553 //\r
1554 // Traverse the array of descriptors in MemoryMap\r
1555 //\r
1556 for (; MemoryMap != MemoryMapDescriptor; MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize)) {\r
1557 //\r
1558 // Check to see if the Type fields are identical.\r
1559 //\r
1560 if (MemoryMap->Type != MemoryMapDescriptor->Type) {\r
1561 continue;\r
1562 }\r
1563\r
1564 //\r
1565 // Check to see if the Attribute fields are identical.\r
1566 //\r
1567 if (MemoryMap->Attribute != MemoryMapDescriptor->Attribute) {\r
1568 continue;\r
1569 }\r
1570\r
1571 //\r
1572 // Check to see if MemoryMapDescriptor is immediately above MemoryMap\r
1573 //\r
d1102dba 1574 if (MemoryMap->PhysicalStart + EFI_PAGES_TO_SIZE ((UINTN)MemoryMap->NumberOfPages) == MemoryMapDescriptor->PhysicalStart) {\r
2345e7d4 1575 //\r
1576 // Merge MemoryMapDescriptor into MemoryMap\r
1577 //\r
1578 MemoryMap->NumberOfPages += MemoryMapDescriptor->NumberOfPages;\r
1579\r
1580 //\r
1581 // Return MemoryMapDescriptor as the next available slot int he MemoryMap array\r
1582 //\r
1583 return MemoryMapDescriptor;\r
1584 }\r
1585\r
1586 //\r
1587 // Check to see if MemoryMapDescriptor is immediately below MemoryMap\r
1588 //\r
1589 if (MemoryMap->PhysicalStart - EFI_PAGES_TO_SIZE ((UINTN)MemoryMapDescriptor->NumberOfPages) == MemoryMapDescriptor->PhysicalStart) {\r
1590 //\r
1591 // Merge MemoryMapDescriptor into MemoryMap\r
1592 //\r
1593 MemoryMap->PhysicalStart = MemoryMapDescriptor->PhysicalStart;\r
1594 MemoryMap->VirtualStart = MemoryMapDescriptor->VirtualStart;\r
1595 MemoryMap->NumberOfPages += MemoryMapDescriptor->NumberOfPages;\r
1596\r
1597 //\r
1598 // Return MemoryMapDescriptor as the next available slot int he MemoryMap array\r
1599 //\r
1600 return MemoryMapDescriptor;\r
1601 }\r
1602 }\r
1603\r
1604 //\r
1605 // MemoryMapDescrtiptor could not be merged with any descriptors in MemoryMap.\r
1606 //\r
d1102dba 1607 // Return the slot immediately after MemoryMapDescriptor as the next available\r
2345e7d4 1608 // slot in the MemoryMap array\r
1609 //\r
1610 return NEXT_MEMORY_DESCRIPTOR (MemoryMapDescriptor, DescriptorSize);\r
1611}\r
28a00297 1612\r
162ed594 1613/**\r
1614 This function returns a copy of the current memory map. The map is an array of\r
1615 memory descriptors, each of which describes a contiguous block of memory.\r
1616\r
022c6d45 1617 @param MemoryMapSize A pointer to the size, in bytes, of the\r
1618 MemoryMap buffer. On input, this is the size of\r
1619 the buffer allocated by the caller. On output,\r
1620 it is the size of the buffer returned by the\r
1621 firmware if the buffer was large enough, or the\r
1622 size of the buffer needed to contain the map if\r
1623 the buffer was too small.\r
1624 @param MemoryMap A pointer to the buffer in which firmware places\r
1625 the current memory map.\r
1626 @param MapKey A pointer to the location in which firmware\r
1627 returns the key for the current memory map.\r
1628 @param DescriptorSize A pointer to the location in which firmware\r
1629 returns the size, in bytes, of an individual\r
1630 EFI_MEMORY_DESCRIPTOR.\r
1631 @param DescriptorVersion A pointer to the location in which firmware\r
1632 returns the version number associated with the\r
1633 EFI_MEMORY_DESCRIPTOR.\r
1634\r
1635 @retval EFI_SUCCESS The memory map was returned in the MemoryMap\r
1636 buffer.\r
1637 @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current\r
1638 buffer size needed to hold the memory map is\r
1639 returned in MemoryMapSize.\r
162ed594 1640 @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value.\r
1641\r
1642**/\r
28a00297 1643EFI_STATUS\r
1644EFIAPI\r
1645CoreGetMemoryMap (\r
1646 IN OUT UINTN *MemoryMapSize,\r
1647 IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap,\r
1648 OUT UINTN *MapKey,\r
1649 OUT UINTN *DescriptorSize,\r
1650 OUT UINT32 *DescriptorVersion\r
1651 )\r
28a00297 1652{\r
1653 EFI_STATUS Status;\r
022c6d45 1654 UINTN Size;\r
1655 UINTN BufferSize;\r
ba2c0527 1656 UINTN NumberOfEntries;\r
28a00297 1657 LIST_ENTRY *Link;\r
022c6d45 1658 MEMORY_MAP *Entry;\r
1659 EFI_GCD_MAP_ENTRY *GcdMapEntry;\r
46a65f18 1660 EFI_GCD_MAP_ENTRY MergeGcdMapEntry;\r
b74350e9 1661 EFI_MEMORY_TYPE Type;\r
2345e7d4 1662 EFI_MEMORY_DESCRIPTOR *MemoryMapStart;\r
e38451cd 1663 EFI_MEMORY_DESCRIPTOR *MemoryMapEnd;\r
28a00297 1664\r
1665 //\r
1666 // Make sure the parameters are valid\r
1667 //\r
1668 if (MemoryMapSize == NULL) {\r
1669 return EFI_INVALID_PARAMETER;\r
1670 }\r
022c6d45 1671\r
28a00297 1672 CoreAcquireGcdMemoryLock ();\r
022c6d45 1673\r
28a00297 1674 //\r
ba2c0527 1675 // Count the number of Reserved and runtime MMIO entries\r
a671a012 1676 // And, count the number of Persistent entries.\r
28a00297 1677 //\r
ba2c0527 1678 NumberOfEntries = 0;\r
28a00297 1679 for (Link = mGcdMemorySpaceMap.ForwardLink; Link != &mGcdMemorySpaceMap; Link = Link->ForwardLink) {\r
1680 GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);\r
d1102dba 1681 if ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypePersistent) ||\r
ba2c0527
LG
1682 (GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeReserved) ||\r
1683 ((GcdMapEntry->GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&\r
1684 ((GcdMapEntry->Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME))) {\r
1685 NumberOfEntries ++;\r
a671a012 1686 }\r
28a00297 1687 }\r
1688\r
1689 Size = sizeof (EFI_MEMORY_DESCRIPTOR);\r
1690\r
1691 //\r
1692 // Make sure Size != sizeof(EFI_MEMORY_DESCRIPTOR). This will\r
1693 // prevent people from having pointer math bugs in their code.\r
1694 // now you have to use *DescriptorSize to make things work.\r
1695 //\r
1696 Size += sizeof(UINT64) - (Size % sizeof (UINT64));\r
1697\r
1698 if (DescriptorSize != NULL) {\r
1699 *DescriptorSize = Size;\r
1700 }\r
022c6d45 1701\r
28a00297 1702 if (DescriptorVersion != NULL) {\r
1703 *DescriptorVersion = EFI_MEMORY_DESCRIPTOR_VERSION;\r
1704 }\r
1705\r
1706 CoreAcquireMemoryLock ();\r
1707\r
1708 //\r
1709 // Compute the buffer size needed to fit the entire map\r
1710 //\r
ba2c0527 1711 BufferSize = Size * NumberOfEntries;\r
28a00297 1712 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
1713 BufferSize += Size;\r
1714 }\r
1715\r
1716 if (*MemoryMapSize < BufferSize) {\r
1717 Status = EFI_BUFFER_TOO_SMALL;\r
1718 goto Done;\r
1719 }\r
1720\r
1721 if (MemoryMap == NULL) {\r
1722 Status = EFI_INVALID_PARAMETER;\r
1723 goto Done;\r
1724 }\r
1725\r
1726 //\r
1727 // Build the map\r
1728 //\r
383c303c 1729 ZeroMem (MemoryMap, BufferSize);\r
2345e7d4 1730 MemoryMapStart = MemoryMap;\r
28a00297 1731 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
1732 Entry = CR (Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
1733 ASSERT (Entry->VirtualStart == 0);\r
1734\r
b74350e9 1735 //\r
1736 // Convert internal map into an EFI_MEMORY_DESCRIPTOR\r
1737 //\r
28a00297 1738 MemoryMap->Type = Entry->Type;\r
1739 MemoryMap->PhysicalStart = Entry->Start;\r
1740 MemoryMap->VirtualStart = Entry->VirtualStart;\r
1741 MemoryMap->NumberOfPages = RShiftU64 (Entry->End - Entry->Start + 1, EFI_PAGE_SHIFT);\r
b74350e9 1742 //\r
1743 // If the memory type is EfiConventionalMemory, then determine if the range is part of a\r
022c6d45 1744 // memory type bin and needs to be converted to the same memory type as the rest of the\r
1745 // memory type bin in order to minimize EFI Memory Map changes across reboots. This\r
b74350e9 1746 // improves the chances for a successful S4 resume in the presence of minor page allocation\r
1747 // differences across reboots.\r
1748 //\r
1749 if (MemoryMap->Type == EfiConventionalMemory) {\r
1750 for (Type = (EFI_MEMORY_TYPE) 0; Type < EfiMaxMemoryType; Type++) {\r
1751 if (mMemoryTypeStatistics[Type].Special &&\r
1752 mMemoryTypeStatistics[Type].NumberOfPages > 0 &&\r
1753 Entry->Start >= mMemoryTypeStatistics[Type].BaseAddress &&\r
e94a9ff7 1754 Entry->End <= mMemoryTypeStatistics[Type].MaximumAddress) {\r
b74350e9 1755 MemoryMap->Type = Type;\r
1756 }\r
1757 }\r
1758 }\r
1759 MemoryMap->Attribute = Entry->Attribute;\r
10fe0d81
RN
1760 if (MemoryMap->Type < EfiMaxMemoryType) {\r
1761 if (mMemoryTypeStatistics[MemoryMap->Type].Runtime) {\r
1762 MemoryMap->Attribute |= EFI_MEMORY_RUNTIME;\r
1763 }\r
28a00297 1764 }\r
022c6d45 1765\r
2345e7d4 1766 //\r
d1102dba 1767 // Check to see if the new Memory Map Descriptor can be merged with an\r
2345e7d4 1768 // existing descriptor if they are adjacent and have the same attributes\r
1769 //\r
1770 MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);\r
28a00297 1771 }\r
1772\r
d1102dba 1773\r
46a65f18
LG
1774 ZeroMem (&MergeGcdMapEntry, sizeof (MergeGcdMapEntry));\r
1775 GcdMapEntry = NULL;\r
1776 for (Link = mGcdMemorySpaceMap.ForwardLink; ; Link = Link->ForwardLink) {\r
1777 if (Link != &mGcdMemorySpaceMap) {\r
1778 //\r
1779 // Merge adjacent same type and attribute GCD memory range\r
1780 //\r
1781 GcdMapEntry = CR (Link, EFI_GCD_MAP_ENTRY, Link, EFI_GCD_MAP_SIGNATURE);\r
d1102dba
LG
1782\r
1783 if ((MergeGcdMapEntry.Capabilities == GcdMapEntry->Capabilities) &&\r
46a65f18
LG
1784 (MergeGcdMapEntry.Attributes == GcdMapEntry->Attributes) &&\r
1785 (MergeGcdMapEntry.GcdMemoryType == GcdMapEntry->GcdMemoryType) &&\r
1786 (MergeGcdMapEntry.GcdIoType == GcdMapEntry->GcdIoType)) {\r
1787 MergeGcdMapEntry.EndAddress = GcdMapEntry->EndAddress;\r
1788 continue;\r
1789 }\r
1790 }\r
1791\r
1792 if ((MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) ||\r
1793 ((MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) &&\r
1794 ((MergeGcdMapEntry.Attributes & EFI_MEMORY_RUNTIME) == EFI_MEMORY_RUNTIME))) {\r
1795 //\r
d1102dba
LG
1796 // Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR,\r
1797 // it will be recorded as page PhysicalStart and NumberOfPages.\r
46a65f18
LG
1798 //\r
1799 ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0);\r
1800 ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0);\r
d1102dba
LG
1801\r
1802 //\r
ba2c0527
LG
1803 // Create EFI_MEMORY_DESCRIPTOR for every Reserved and runtime MMIO GCD entries\r
1804 //\r
46a65f18 1805 MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress;\r
ba2c0527 1806 MemoryMap->VirtualStart = 0;\r
46a65f18 1807 MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT);\r
d1102dba 1808 MemoryMap->Attribute = (MergeGcdMapEntry.Attributes & ~EFI_MEMORY_PORT_IO) |\r
46a65f18 1809 (MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO |\r
ff0c6d66 1810 EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB));\r
ba2c0527 1811\r
46a65f18 1812 if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeReserved) {\r
ba2c0527 1813 MemoryMap->Type = EfiReservedMemoryType;\r
46a65f18
LG
1814 } else if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypeMemoryMappedIo) {\r
1815 if ((MergeGcdMapEntry.Attributes & EFI_MEMORY_PORT_IO) == EFI_MEMORY_PORT_IO) {\r
ba2c0527
LG
1816 MemoryMap->Type = EfiMemoryMappedIOPortSpace;\r
1817 } else {\r
1818 MemoryMap->Type = EfiMemoryMappedIO;\r
28a00297 1819 }\r
28a00297 1820 }\r
ba2c0527
LG
1821\r
1822 //\r
d1102dba 1823 // Check to see if the new Memory Map Descriptor can be merged with an\r
ba2c0527
LG
1824 // existing descriptor if they are adjacent and have the same attributes\r
1825 //\r
1826 MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);\r
28a00297 1827 }\r
d1102dba 1828\r
35ac962b 1829 if (MergeGcdMapEntry.GcdMemoryType == EfiGcdMemoryTypePersistent) {\r
46a65f18 1830 //\r
d1102dba
LG
1831 // Page Align GCD range is required. When it is converted to EFI_MEMORY_DESCRIPTOR,\r
1832 // it will be recorded as page PhysicalStart and NumberOfPages.\r
46a65f18
LG
1833 //\r
1834 ASSERT ((MergeGcdMapEntry.BaseAddress & EFI_PAGE_MASK) == 0);\r
1835 ASSERT (((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1) & EFI_PAGE_MASK) == 0);\r
1836\r
d1102dba 1837 //\r
a671a012
LG
1838 // Create EFI_MEMORY_DESCRIPTOR for every Persistent GCD entries\r
1839 //\r
46a65f18 1840 MemoryMap->PhysicalStart = MergeGcdMapEntry.BaseAddress;\r
a671a012 1841 MemoryMap->VirtualStart = 0;\r
46a65f18 1842 MemoryMap->NumberOfPages = RShiftU64 ((MergeGcdMapEntry.EndAddress - MergeGcdMapEntry.BaseAddress + 1), EFI_PAGE_SHIFT);\r
d1102dba 1843 MemoryMap->Attribute = MergeGcdMapEntry.Attributes | EFI_MEMORY_NV |\r
46a65f18 1844 (MergeGcdMapEntry.Capabilities & (EFI_MEMORY_RP | EFI_MEMORY_WP | EFI_MEMORY_XP | EFI_MEMORY_RO |\r
ff0c6d66 1845 EFI_MEMORY_UC | EFI_MEMORY_UCE | EFI_MEMORY_WC | EFI_MEMORY_WT | EFI_MEMORY_WB));\r
a671a012 1846 MemoryMap->Type = EfiPersistentMemory;\r
d1102dba 1847\r
a671a012 1848 //\r
d1102dba 1849 // Check to see if the new Memory Map Descriptor can be merged with an\r
a671a012
LG
1850 // existing descriptor if they are adjacent and have the same attributes\r
1851 //\r
1852 MemoryMap = MergeMemoryMapDescriptor (MemoryMapStart, MemoryMap, Size);\r
1853 }\r
46a65f18
LG
1854 if (Link == &mGcdMemorySpaceMap) {\r
1855 //\r
1856 // break loop when arrive at head.\r
1857 //\r
1858 break;\r
1859 }\r
1860 if (GcdMapEntry != NULL) {\r
1861 //\r
1862 // Copy new GCD map entry for the following GCD range merge\r
1863 //\r
1864 CopyMem (&MergeGcdMapEntry, GcdMapEntry, sizeof (MergeGcdMapEntry));\r
1865 }\r
28a00297 1866 }\r
022c6d45 1867\r
2345e7d4 1868 //\r
1869 // Compute the size of the buffer actually used after all memory map descriptor merge operations\r
1870 //\r
1871 BufferSize = ((UINT8 *)MemoryMap - (UINT8 *)MemoryMapStart);\r
1872\r
e38451cd
JW
1873 //\r
1874 // Note: Some OSs will treat EFI_MEMORY_DESCRIPTOR.Attribute as really\r
1875 // set attributes and change memory paging attribute accordingly.\r
1876 // But current EFI_MEMORY_DESCRIPTOR.Attribute is assigned by\r
1877 // value from Capabilities in GCD memory map. This might cause\r
1878 // boot problems. Clearing all paging related capabilities can\r
1879 // workaround it. Following code is supposed to be removed once\r
1880 // the usage of EFI_MEMORY_DESCRIPTOR.Attribute is clarified in\r
1881 // UEFI spec and adopted by both EDK-II Core and all supported\r
1882 // OSs.\r
1883 //\r
1884 MemoryMapEnd = MemoryMap;\r
1885 MemoryMap = MemoryMapStart;\r
1886 while (MemoryMap < MemoryMapEnd) {\r
1887 MemoryMap->Attribute &= ~(UINT64)(EFI_MEMORY_RP | EFI_MEMORY_RO |\r
1888 EFI_MEMORY_XP);\r
1889 MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, Size);\r
1890 }\r
646127c1
JW
1891 MergeMemoryMap (MemoryMapStart, &BufferSize, Size);\r
1892 MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMapStart + BufferSize);\r
e38451cd 1893\r
28a00297 1894 Status = EFI_SUCCESS;\r
1895\r
1896Done:\r
022c6d45 1897 //\r
1898 // Update the map key finally\r
1899 //\r
28a00297 1900 if (MapKey != NULL) {\r
1901 *MapKey = mMemoryMapKey;\r
1902 }\r
022c6d45 1903\r
e439df50 1904 CoreReleaseMemoryLock ();\r
1905\r
1906 CoreReleaseGcdMemoryLock ();\r
1907\r
28a00297 1908 *MemoryMapSize = BufferSize;\r
022c6d45 1909\r
e63da9f0
JW
1910 DEBUG_CODE (\r
1911 if (PcdGet8 (PcdHeapGuardPropertyMask) & (BIT1|BIT0)) {\r
1912 DumpGuardedMemoryBitmap ();\r
1913 }\r
1914 );\r
1915\r
28a00297 1916 return Status;\r
1917}\r
1918\r
28a00297 1919\r
162ed594 1920/**\r
28a00297 1921 Internal function. Used by the pool functions to allocate pages\r
1922 to back pool allocation requests.\r
1923\r
022c6d45 1924 @param PoolType The type of memory for the new pool pages\r
1925 @param NumberOfPages No of pages to allocate\r
1926 @param Alignment Bits to align.\r
e63da9f0 1927 @param NeedGuard Flag to indicate Guard page is needed or not\r
28a00297 1928\r
162ed594 1929 @return The allocated memory, or NULL\r
28a00297 1930\r
162ed594 1931**/\r
1932VOID *\r
1933CoreAllocatePoolPages (\r
1934 IN EFI_MEMORY_TYPE PoolType,\r
1935 IN UINTN NumberOfPages,\r
e63da9f0
JW
1936 IN UINTN Alignment,\r
1937 IN BOOLEAN NeedGuard\r
162ed594 1938 )\r
28a00297 1939{\r
1940 UINT64 Start;\r
1941\r
1942 //\r
1943 // Find the pages to convert\r
1944 //\r
e63da9f0
JW
1945 Start = FindFreePages (MAX_ADDRESS, NumberOfPages, PoolType, Alignment,\r
1946 NeedGuard);\r
28a00297 1947\r
1948 //\r
1949 // Convert it to boot services data\r
1950 //\r
1951 if (Start == 0) {\r
7df7393f 1952 DEBUG ((DEBUG_ERROR | DEBUG_PAGE, "AllocatePoolPages: failed to allocate %d pages\n", (UINT32)NumberOfPages));\r
28a00297 1953 } else {\r
e63da9f0
JW
1954 if (NeedGuard) {\r
1955 CoreConvertPagesWithGuard (Start, NumberOfPages, PoolType);\r
1956 } else {\r
1957 CoreConvertPages (Start, NumberOfPages, PoolType);\r
1958 }\r
28a00297 1959 }\r
1960\r
e94a9ff7 1961 return (VOID *)(UINTN) Start;\r
28a00297 1962}\r
1963\r
162ed594 1964\r
1965/**\r
1966 Internal function. Frees pool pages allocated via AllocatePoolPages ()\r
1967\r
022c6d45 1968 @param Memory The base address to free\r
162ed594 1969 @param NumberOfPages The number of pages to free\r
1970\r
1971**/\r
28a00297 1972VOID\r
1973CoreFreePoolPages (\r
1974 IN EFI_PHYSICAL_ADDRESS Memory,\r
1975 IN UINTN NumberOfPages\r
1976 )\r
28a00297 1977{\r
1978 CoreConvertPages (Memory, NumberOfPages, EfiConventionalMemory);\r
1979}\r
1980\r
1981\r
28a00297 1982\r
162ed594 1983/**\r
1984 Make sure the memory map is following all the construction rules,\r
28a00297 1985 it is the last time to check memory map error before exit boot services.\r
1986\r
022c6d45 1987 @param MapKey Memory map key\r
28a00297 1988\r
022c6d45 1989 @retval EFI_INVALID_PARAMETER Memory map not consistent with construction\r
1990 rules.\r
162ed594 1991 @retval EFI_SUCCESS Valid memory map.\r
28a00297 1992\r
162ed594 1993**/\r
1994EFI_STATUS\r
1995CoreTerminateMemoryMap (\r
1996 IN UINTN MapKey\r
1997 )\r
28a00297 1998{\r
1999 EFI_STATUS Status;\r
2000 LIST_ENTRY *Link;\r
2001 MEMORY_MAP *Entry;\r
2002\r
2003 Status = EFI_SUCCESS;\r
2004\r
2005 CoreAcquireMemoryLock ();\r
2006\r
2007 if (MapKey == mMemoryMapKey) {\r
2008\r
2009 //\r
2010 // Make sure the memory map is following all the construction rules\r
2011 // This is the last chance we will be able to display any messages on\r
2012 // the console devices.\r
2013 //\r
2014\r
2015 for (Link = gMemoryMap.ForwardLink; Link != &gMemoryMap; Link = Link->ForwardLink) {\r
2016 Entry = CR(Link, MEMORY_MAP, Link, MEMORY_MAP_SIGNATURE);\r
7d17a6a1
AB
2017 if (Entry->Type < EfiMaxMemoryType) {\r
2018 if (mMemoryTypeStatistics[Entry->Type].Runtime) {\r
2019 ASSERT (Entry->Type != EfiACPIReclaimMemory);\r
2020 ASSERT (Entry->Type != EfiACPIMemoryNVS);\r
d4731a98 2021 if ((Entry->Start & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) {\r
7d17a6a1
AB
2022 DEBUG((DEBUG_ERROR | DEBUG_PAGE, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n"));\r
2023 Status = EFI_INVALID_PARAMETER;\r
2024 goto Done;\r
2025 }\r
d4731a98 2026 if (((Entry->End + 1) & (RUNTIME_PAGE_ALLOCATION_GRANULARITY - 1)) != 0) {\r
7d17a6a1
AB
2027 DEBUG((DEBUG_ERROR | DEBUG_PAGE, "ExitBootServices: A RUNTIME memory entry is not on a proper alignment.\n"));\r
2028 Status = EFI_INVALID_PARAMETER;\r
2029 goto Done;\r
2030 }\r
28a00297 2031 }\r
2032 }\r
2033 }\r
2034\r
2035 //\r
2036 // The map key they gave us matches what we expect. Fall through and\r
2037 // return success. In an ideal world we would clear out all of\r
2038 // EfiBootServicesCode and EfiBootServicesData. However this function\r
2039 // is not the last one called by ExitBootServices(), so we have to\r
2040 // preserve the memory contents.\r
2041 //\r
2042 } else {\r
2043 Status = EFI_INVALID_PARAMETER;\r
2044 }\r
2045\r
d45fd260 2046Done:\r
28a00297 2047 CoreReleaseMemoryLock ();\r
2048\r
2049 return Status;\r
2050}\r
2051\r
2052\r
2053\r
2054\r
2055\r
2056\r
2057\r
2058\r
162ed594 2059\r