]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Core/Pei/Memory/MemoryServices.c
MdeModulePkg/PeiCore: Enable T-RAM evacuation in PeiCore (CVE-2019-11098)
[mirror_edk2.git] / MdeModulePkg / Core / Pei / Memory / MemoryServices.c
1 /** @file
2 EFI PEI Core memory services
3
4 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "PeiMain.h"
10
11 /**
12
13 Initialize the memory services.
14
15 @param PrivateData Points to PeiCore's private instance data.
16 @param SecCoreData Points to a data structure containing information about the PEI core's operating
17 environment, such as the size and location of temporary RAM, the stack location and
18 the BFV location.
19 @param OldCoreData Pointer to the PEI Core data.
20 NULL if being run in non-permanent memory mode.
21
22 **/
23 VOID
24 InitializeMemoryServices (
25 IN PEI_CORE_INSTANCE *PrivateData,
26 IN CONST EFI_SEC_PEI_HAND_OFF *SecCoreData,
27 IN PEI_CORE_INSTANCE *OldCoreData
28 )
29 {
30
31 PrivateData->SwitchStackSignal = FALSE;
32
33 //
34 // First entering PeiCore, following code will initialized some field
35 // in PeiCore's private data according to hand off data from SEC core.
36 //
37 if (OldCoreData == NULL) {
38
39 PrivateData->PeiMemoryInstalled = FALSE;
40 PrivateData->HobList.Raw = SecCoreData->PeiTemporaryRamBase;
41
42 PeiCoreBuildHobHandoffInfoTable (
43 BOOT_WITH_FULL_CONFIGURATION,
44 (EFI_PHYSICAL_ADDRESS) (UINTN) SecCoreData->PeiTemporaryRamBase,
45 (UINTN) SecCoreData->PeiTemporaryRamSize
46 );
47
48 //
49 // Set Ps to point to ServiceTableShadow in Cache
50 //
51 PrivateData->Ps = &(PrivateData->ServiceTableShadow);
52 }
53
54 return;
55 }
56
57 /**
58
59 This function registers the found memory configuration with the PEI Foundation.
60
61 The usage model is that the PEIM that discovers the permanent memory shall invoke this service.
62 This routine will hold discoveried memory information into PeiCore's private data,
63 and set SwitchStackSignal flag. After PEIM who discovery memory is dispatched,
64 PeiDispatcher will migrate temporary memory to permanent memory.
65
66 @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
67 @param MemoryBegin Start of memory address.
68 @param MemoryLength Length of memory.
69
70 @return EFI_SUCCESS Always success.
71
72 **/
73 EFI_STATUS
74 EFIAPI
75 PeiInstallPeiMemory (
76 IN CONST EFI_PEI_SERVICES **PeiServices,
77 IN EFI_PHYSICAL_ADDRESS MemoryBegin,
78 IN UINT64 MemoryLength
79 )
80 {
81 PEI_CORE_INSTANCE *PrivateData;
82
83 DEBUG ((EFI_D_INFO, "PeiInstallPeiMemory MemoryBegin 0x%LX, MemoryLength 0x%LX\n", MemoryBegin, MemoryLength));
84 PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices);
85
86 //
87 // PEI_SERVICE.InstallPeiMemory should only be called one time during whole PEI phase.
88 // If it is invoked more than one time, ASSERT information is given for developer debugging in debug tip and
89 // simply return EFI_SUCCESS in release tip to ignore it.
90 //
91 if (PrivateData->PeiMemoryInstalled) {
92 DEBUG ((EFI_D_ERROR, "ERROR: PeiInstallPeiMemory is called more than once!\n"));
93 ASSERT (FALSE);
94 return EFI_SUCCESS;
95 }
96
97 PrivateData->PhysicalMemoryBegin = MemoryBegin;
98 PrivateData->PhysicalMemoryLength = MemoryLength;
99 PrivateData->FreePhysicalMemoryTop = MemoryBegin + MemoryLength;
100
101 PrivateData->SwitchStackSignal = TRUE;
102
103 return EFI_SUCCESS;
104 }
105
106 /**
107 Migrate memory pages allocated in pre-memory phase.
108 Copy memory pages at temporary heap top to permanent heap top.
109
110 @param[in] Private Pointer to the private data passed in from caller.
111 @param[in] TemporaryRamMigrated Temporary memory has been migrated to permanent memory.
112
113 **/
114 VOID
115 MigrateMemoryPages (
116 IN PEI_CORE_INSTANCE *Private,
117 IN BOOLEAN TemporaryRamMigrated
118 )
119 {
120 EFI_PHYSICAL_ADDRESS NewMemPagesBase;
121 EFI_PHYSICAL_ADDRESS MemPagesBase;
122
123 Private->MemoryPages.Size = (UINTN) (Private->HobList.HandoffInformationTable->EfiMemoryTop -
124 Private->HobList.HandoffInformationTable->EfiFreeMemoryTop);
125 if (Private->MemoryPages.Size == 0) {
126 //
127 // No any memory page allocated in pre-memory phase.
128 //
129 return;
130 }
131 Private->MemoryPages.Base = Private->HobList.HandoffInformationTable->EfiFreeMemoryTop;
132
133 ASSERT (Private->MemoryPages.Size <= Private->FreePhysicalMemoryTop);
134 NewMemPagesBase = Private->FreePhysicalMemoryTop - Private->MemoryPages.Size;
135 NewMemPagesBase &= ~(UINT64)EFI_PAGE_MASK;
136 ASSERT (NewMemPagesBase >= Private->PhysicalMemoryBegin);
137 //
138 // Copy memory pages at temporary heap top to permanent heap top.
139 //
140 if (TemporaryRamMigrated) {
141 //
142 // Memory pages at temporary heap top has been migrated to permanent heap,
143 // Here still needs to copy them from permanent heap to permanent heap top.
144 //
145 MemPagesBase = Private->MemoryPages.Base;
146 if (Private->HeapOffsetPositive) {
147 MemPagesBase += Private->HeapOffset;
148 } else {
149 MemPagesBase -= Private->HeapOffset;
150 }
151 CopyMem ((VOID *)(UINTN)NewMemPagesBase, (VOID *)(UINTN)MemPagesBase, Private->MemoryPages.Size);
152 } else {
153 CopyMem ((VOID *)(UINTN)NewMemPagesBase, (VOID *)(UINTN)Private->MemoryPages.Base, Private->MemoryPages.Size);
154 }
155
156 if (NewMemPagesBase >= Private->MemoryPages.Base) {
157 Private->MemoryPages.OffsetPositive = TRUE;
158 Private->MemoryPages.Offset = (UINTN)(NewMemPagesBase - Private->MemoryPages.Base);
159 } else {
160 Private->MemoryPages.OffsetPositive = FALSE;
161 Private->MemoryPages.Offset = (UINTN)(Private->MemoryPages.Base - NewMemPagesBase);
162 }
163
164 DEBUG ((DEBUG_INFO, "Pages Offset = 0x%lX\n", (UINT64) Private->MemoryPages.Offset));
165
166 Private->FreePhysicalMemoryTop = NewMemPagesBase;
167 }
168
169 /**
170 Removes any FV HOBs whose base address is not in PEI installed memory.
171
172 @param[in] Private Pointer to PeiCore's private data structure.
173
174 **/
175 VOID
176 RemoveFvHobsInTemporaryMemory (
177 IN PEI_CORE_INSTANCE *Private
178 )
179 {
180 EFI_PEI_HOB_POINTERS Hob;
181 EFI_HOB_FIRMWARE_VOLUME *FirmwareVolumeHob;
182
183 DEBUG ((DEBUG_INFO, "Removing FVs in FV HOB not already migrated to permanent memory.\n"));
184
185 for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
186 if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV || GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV2 || GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV3) {
187 FirmwareVolumeHob = Hob.FirmwareVolume;
188 DEBUG ((DEBUG_INFO, " Found FV HOB.\n"));
189 DEBUG ((
190 DEBUG_INFO,
191 " BA=%016lx L=%016lx\n",
192 FirmwareVolumeHob->BaseAddress,
193 FirmwareVolumeHob->Length
194 ));
195 if (
196 !(
197 ((EFI_PHYSICAL_ADDRESS) (UINTN) FirmwareVolumeHob->BaseAddress >= Private->PhysicalMemoryBegin) &&
198 (((EFI_PHYSICAL_ADDRESS) (UINTN) FirmwareVolumeHob->BaseAddress + (FirmwareVolumeHob->Length - 1)) < Private->FreePhysicalMemoryTop)
199 )
200 ) {
201 DEBUG ((DEBUG_INFO, " Removing FV HOB to an FV in T-RAM (was not migrated).\n"));
202 Hob.Header->HobType = EFI_HOB_TYPE_UNUSED;
203 }
204 }
205 }
206 }
207
208 /**
209 Migrate the base address in firmware volume allocation HOBs
210 from temporary memory to PEI installed memory.
211
212 @param[in] PrivateData Pointer to PeiCore's private data structure.
213 @param[in] OrgFvHandle Address of FV Handle in temporary memory.
214 @param[in] FvHandle Address of FV Handle in permanent memory.
215
216 **/
217 VOID
218 ConvertFvHob (
219 IN PEI_CORE_INSTANCE *PrivateData,
220 IN UINTN OrgFvHandle,
221 IN UINTN FvHandle
222 )
223 {
224 EFI_PEI_HOB_POINTERS Hob;
225 EFI_HOB_FIRMWARE_VOLUME *FirmwareVolumeHob;
226 EFI_HOB_FIRMWARE_VOLUME2 *FirmwareVolume2Hob;
227 EFI_HOB_FIRMWARE_VOLUME3 *FirmwareVolume3Hob;
228
229 DEBUG ((DEBUG_INFO, "Converting FVs in FV HOB.\n"));
230
231 for (Hob.Raw = GetHobList (); !END_OF_HOB_LIST (Hob); Hob.Raw = GET_NEXT_HOB (Hob)) {
232 if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV) {
233 FirmwareVolumeHob = Hob.FirmwareVolume;
234 if (FirmwareVolumeHob->BaseAddress == OrgFvHandle) {
235 FirmwareVolumeHob->BaseAddress = FvHandle;
236 }
237 } else if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV2) {
238 FirmwareVolume2Hob = Hob.FirmwareVolume2;
239 if (FirmwareVolume2Hob->BaseAddress == OrgFvHandle) {
240 FirmwareVolume2Hob->BaseAddress = FvHandle;
241 }
242 } else if (GET_HOB_TYPE (Hob) == EFI_HOB_TYPE_FV3) {
243 FirmwareVolume3Hob = Hob.FirmwareVolume3;
244 if (FirmwareVolume3Hob->BaseAddress == OrgFvHandle) {
245 FirmwareVolume3Hob->BaseAddress = FvHandle;
246 }
247 }
248 }
249 }
250
251 /**
252 Migrate MemoryBaseAddress in memory allocation HOBs
253 from the temporary memory to PEI installed memory.
254
255 @param[in] PrivateData Pointer to PeiCore's private data structure.
256
257 **/
258 VOID
259 ConvertMemoryAllocationHobs (
260 IN PEI_CORE_INSTANCE *PrivateData
261 )
262 {
263 EFI_PEI_HOB_POINTERS Hob;
264 EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
265 EFI_PHYSICAL_ADDRESS OldMemPagesBase;
266 UINTN OldMemPagesSize;
267
268 if (PrivateData->MemoryPages.Size == 0) {
269 //
270 // No any memory page allocated in pre-memory phase.
271 //
272 return;
273 }
274
275 OldMemPagesBase = PrivateData->MemoryPages.Base;
276 OldMemPagesSize = PrivateData->MemoryPages.Size;
277
278 MemoryAllocationHob = NULL;
279 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
280 while (Hob.Raw != NULL) {
281 MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *) Hob.Raw;
282 if ((MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress >= OldMemPagesBase) &&
283 (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress < (OldMemPagesBase + OldMemPagesSize))
284 ) {
285 if (PrivateData->MemoryPages.OffsetPositive) {
286 MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress += PrivateData->MemoryPages.Offset;
287 } else {
288 MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress -= PrivateData->MemoryPages.Offset;
289 }
290 }
291
292 Hob.Raw = GET_NEXT_HOB (Hob);
293 Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw);
294 }
295 }
296
297 /**
298 Internal function to build a HOB for the memory allocation.
299 It will search and reuse the unused(freed) memory allocation HOB,
300 or build memory allocation HOB normally if no unused(freed) memory allocation HOB found.
301
302 @param[in] BaseAddress The 64 bit physical address of the memory.
303 @param[in] Length The length of the memory allocation in bytes.
304 @param[in] MemoryType The type of memory allocated by this HOB.
305
306 **/
307 VOID
308 InternalBuildMemoryAllocationHob (
309 IN EFI_PHYSICAL_ADDRESS BaseAddress,
310 IN UINT64 Length,
311 IN EFI_MEMORY_TYPE MemoryType
312 )
313 {
314 EFI_PEI_HOB_POINTERS Hob;
315 EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
316
317 //
318 // Search unused(freed) memory allocation HOB.
319 //
320 MemoryAllocationHob = NULL;
321 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_UNUSED);
322 while (Hob.Raw != NULL) {
323 if (Hob.Header->HobLength == sizeof (EFI_HOB_MEMORY_ALLOCATION)) {
324 MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *) Hob.Raw;
325 break;
326 }
327
328 Hob.Raw = GET_NEXT_HOB (Hob);
329 Hob.Raw = GetNextHob (EFI_HOB_TYPE_UNUSED, Hob.Raw);
330 }
331
332 if (MemoryAllocationHob != NULL) {
333 //
334 // Reuse the unused(freed) memory allocation HOB.
335 //
336 MemoryAllocationHob->Header.HobType = EFI_HOB_TYPE_MEMORY_ALLOCATION;
337 ZeroMem (&(MemoryAllocationHob->AllocDescriptor.Name), sizeof (EFI_GUID));
338 MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress = BaseAddress;
339 MemoryAllocationHob->AllocDescriptor.MemoryLength = Length;
340 MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType;
341 //
342 // Zero the reserved space to match HOB spec
343 //
344 ZeroMem (MemoryAllocationHob->AllocDescriptor.Reserved, sizeof (MemoryAllocationHob->AllocDescriptor.Reserved));
345 } else {
346 //
347 // No unused(freed) memory allocation HOB found.
348 // Build memory allocation HOB normally.
349 //
350 BuildMemoryAllocationHob (
351 BaseAddress,
352 Length,
353 MemoryType
354 );
355 }
356 }
357
358 /**
359 Update or split memory allocation HOB for memory pages allocate and free.
360
361 @param[in, out] MemoryAllocationHob Pointer to the memory allocation HOB
362 that needs to be updated or split.
363 On output, it will be filled with
364 the input Memory, Bytes and MemoryType.
365 @param[in] Memory Memory to allocate or free.
366 @param[in] Bytes Bytes to allocate or free.
367 @param[in] MemoryType EfiConventionalMemory for pages free,
368 others for pages allocate.
369
370 **/
371 VOID
372 UpdateOrSplitMemoryAllocationHob (
373 IN OUT EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob,
374 IN EFI_PHYSICAL_ADDRESS Memory,
375 IN UINT64 Bytes,
376 IN EFI_MEMORY_TYPE MemoryType
377 )
378 {
379 if ((Memory + Bytes) <
380 (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + MemoryAllocationHob->AllocDescriptor.MemoryLength)) {
381 //
382 // Last pages need to be split out.
383 //
384 InternalBuildMemoryAllocationHob (
385 Memory + Bytes,
386 (MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress + MemoryAllocationHob->AllocDescriptor.MemoryLength) - (Memory + Bytes),
387 MemoryAllocationHob->AllocDescriptor.MemoryType
388 );
389 }
390
391 if (Memory > MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress) {
392 //
393 // First pages need to be split out.
394 //
395 InternalBuildMemoryAllocationHob (
396 MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress,
397 Memory - MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress,
398 MemoryAllocationHob->AllocDescriptor.MemoryType
399 );
400 }
401
402 //
403 // Update the memory allocation HOB.
404 //
405 MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress = Memory;
406 MemoryAllocationHob->AllocDescriptor.MemoryLength = Bytes;
407 MemoryAllocationHob->AllocDescriptor.MemoryType = MemoryType;
408 }
409
410 /**
411 Merge adjacent free memory ranges in memory allocation HOBs.
412
413 @retval TRUE There are free memory ranges merged.
414 @retval FALSE No free memory ranges merged.
415
416 **/
417 BOOLEAN
418 MergeFreeMemoryInMemoryAllocationHob (
419 VOID
420 )
421 {
422 EFI_PEI_HOB_POINTERS Hob;
423 EFI_PEI_HOB_POINTERS Hob2;
424 EFI_HOB_MEMORY_ALLOCATION *MemoryHob;
425 EFI_HOB_MEMORY_ALLOCATION *MemoryHob2;
426 UINT64 Start;
427 UINT64 End;
428 BOOLEAN Merged;
429
430 Merged = FALSE;
431
432 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
433 while (Hob.Raw != NULL) {
434 if (Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) {
435 MemoryHob = (EFI_HOB_MEMORY_ALLOCATION *) Hob.Raw;
436 Start = MemoryHob->AllocDescriptor.MemoryBaseAddress;
437 End = MemoryHob->AllocDescriptor.MemoryBaseAddress + MemoryHob->AllocDescriptor.MemoryLength;
438
439 Hob2.Raw = GET_NEXT_HOB (Hob);
440 Hob2.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw);
441 while (Hob2.Raw != NULL) {
442 if (Hob2.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) {
443 MemoryHob2 = (EFI_HOB_MEMORY_ALLOCATION *) Hob2.Raw;
444 if (Start == (MemoryHob2->AllocDescriptor.MemoryBaseAddress + MemoryHob2->AllocDescriptor.MemoryLength)) {
445 //
446 // Merge adjacent two free memory ranges.
447 //
448 MemoryHob2->AllocDescriptor.MemoryLength += MemoryHob->AllocDescriptor.MemoryLength;
449 Merged = TRUE;
450 //
451 // Mark MemoryHob to be unused(freed).
452 //
453 MemoryHob->Header.HobType = EFI_HOB_TYPE_UNUSED;
454 break;
455 } else if (End == MemoryHob2->AllocDescriptor.MemoryBaseAddress) {
456 //
457 // Merge adjacent two free memory ranges.
458 //
459 MemoryHob2->AllocDescriptor.MemoryBaseAddress = MemoryHob->AllocDescriptor.MemoryBaseAddress;
460 MemoryHob2->AllocDescriptor.MemoryLength += MemoryHob->AllocDescriptor.MemoryLength;
461 Merged = TRUE;
462 //
463 // Mark MemoryHob to be unused(freed).
464 //
465 MemoryHob->Header.HobType = EFI_HOB_TYPE_UNUSED;
466 break;
467 }
468 }
469 Hob2.Raw = GET_NEXT_HOB (Hob2);
470 Hob2.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob2.Raw);
471 }
472 }
473 Hob.Raw = GET_NEXT_HOB (Hob);
474 Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw);
475 }
476
477 return Merged;
478 }
479
480 /**
481 Find free memory by searching memory allocation HOBs.
482
483 @param[in] MemoryType The type of memory to allocate.
484 @param[in] Pages The number of contiguous 4 KB pages to allocate.
485 @param[in] Granularity Page allocation granularity.
486 @param[out] Memory Pointer to a physical address. On output, the address is set to the base
487 of the page range that was allocated.
488
489 @retval EFI_SUCCESS The memory range was successfully allocated.
490 @retval EFI_NOT_FOUND No memory allocation HOB with big enough free memory found.
491
492 **/
493 EFI_STATUS
494 FindFreeMemoryFromMemoryAllocationHob (
495 IN EFI_MEMORY_TYPE MemoryType,
496 IN UINTN Pages,
497 IN UINTN Granularity,
498 OUT EFI_PHYSICAL_ADDRESS *Memory
499 )
500 {
501 EFI_PEI_HOB_POINTERS Hob;
502 EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
503 UINT64 Bytes;
504 EFI_PHYSICAL_ADDRESS BaseAddress;
505
506 Bytes = LShiftU64 (Pages, EFI_PAGE_SHIFT);
507
508 BaseAddress = 0;
509 MemoryAllocationHob = NULL;
510 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
511 while (Hob.Raw != NULL) {
512 if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) &&
513 (Hob.MemoryAllocation->AllocDescriptor.MemoryLength >= Bytes)) {
514 //
515 // Found one memory allocation HOB with big enough free memory.
516 //
517 MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *) Hob.Raw;
518 BaseAddress = MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress +
519 MemoryAllocationHob->AllocDescriptor.MemoryLength - Bytes;
520 //
521 // Make sure the granularity could be satisfied.
522 //
523 BaseAddress &= ~((EFI_PHYSICAL_ADDRESS) Granularity - 1);
524 if (BaseAddress >= MemoryAllocationHob->AllocDescriptor.MemoryBaseAddress) {
525 break;
526 }
527 BaseAddress = 0;
528 MemoryAllocationHob = NULL;
529 }
530 //
531 // Continue to find.
532 //
533 Hob.Raw = GET_NEXT_HOB (Hob);
534 Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw);
535 }
536
537 if (MemoryAllocationHob != NULL) {
538 UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, BaseAddress, Bytes, MemoryType);
539 *Memory = BaseAddress;
540 return EFI_SUCCESS;
541 } else {
542 if (MergeFreeMemoryInMemoryAllocationHob ()) {
543 //
544 // Retry if there are free memory ranges merged.
545 //
546 return FindFreeMemoryFromMemoryAllocationHob (MemoryType, Pages, Granularity, Memory);
547 }
548 return EFI_NOT_FOUND;
549 }
550 }
551
552 /**
553 The purpose of the service is to publish an interface that allows
554 PEIMs to allocate memory ranges that are managed by the PEI Foundation.
555
556 Prior to InstallPeiMemory() being called, PEI will allocate pages from the heap.
557 After InstallPeiMemory() is called, PEI will allocate pages within the region
558 of memory provided by InstallPeiMemory() service in a best-effort fashion.
559 Location-specific allocations are not managed by the PEI foundation code.
560
561 @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
562 @param MemoryType The type of memory to allocate.
563 @param Pages The number of contiguous 4 KB pages to allocate.
564 @param Memory Pointer to a physical address. On output, the address is set to the base
565 of the page range that was allocated.
566
567 @retval EFI_SUCCESS The memory range was successfully allocated.
568 @retval EFI_OUT_OF_RESOURCES The pages could not be allocated.
569 @retval EFI_INVALID_PARAMETER Type is not equal to EfiLoaderCode, EfiLoaderData, EfiRuntimeServicesCode,
570 EfiRuntimeServicesData, EfiBootServicesCode, EfiBootServicesData,
571 EfiACPIReclaimMemory, EfiReservedMemoryType, or EfiACPIMemoryNVS.
572
573 **/
574 EFI_STATUS
575 EFIAPI
576 PeiAllocatePages (
577 IN CONST EFI_PEI_SERVICES **PeiServices,
578 IN EFI_MEMORY_TYPE MemoryType,
579 IN UINTN Pages,
580 OUT EFI_PHYSICAL_ADDRESS *Memory
581 )
582 {
583 EFI_STATUS Status;
584 PEI_CORE_INSTANCE *PrivateData;
585 EFI_PEI_HOB_POINTERS Hob;
586 EFI_PHYSICAL_ADDRESS *FreeMemoryTop;
587 EFI_PHYSICAL_ADDRESS *FreeMemoryBottom;
588 UINTN RemainingPages;
589 UINTN Granularity;
590 UINTN Padding;
591
592 if ((MemoryType != EfiLoaderCode) &&
593 (MemoryType != EfiLoaderData) &&
594 (MemoryType != EfiRuntimeServicesCode) &&
595 (MemoryType != EfiRuntimeServicesData) &&
596 (MemoryType != EfiBootServicesCode) &&
597 (MemoryType != EfiBootServicesData) &&
598 (MemoryType != EfiACPIReclaimMemory) &&
599 (MemoryType != EfiReservedMemoryType) &&
600 (MemoryType != EfiACPIMemoryNVS)) {
601 return EFI_INVALID_PARAMETER;
602 }
603
604 Granularity = DEFAULT_PAGE_ALLOCATION_GRANULARITY;
605
606 PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices);
607 Hob.Raw = PrivateData->HobList.Raw;
608
609 if (Hob.Raw == NULL) {
610 //
611 // HOB is not initialized yet.
612 //
613 return EFI_NOT_AVAILABLE_YET;
614 }
615
616 if (RUNTIME_PAGE_ALLOCATION_GRANULARITY > DEFAULT_PAGE_ALLOCATION_GRANULARITY &&
617 (MemoryType == EfiACPIReclaimMemory ||
618 MemoryType == EfiACPIMemoryNVS ||
619 MemoryType == EfiRuntimeServicesCode ||
620 MemoryType == EfiRuntimeServicesData)) {
621
622 Granularity = RUNTIME_PAGE_ALLOCATION_GRANULARITY;
623
624 DEBUG ((DEBUG_INFO, "AllocatePages: aligning allocation to %d KB\n",
625 Granularity / SIZE_1KB));
626 }
627
628 if (!PrivateData->PeiMemoryInstalled && PrivateData->SwitchStackSignal) {
629 //
630 // When PeiInstallMemory is called but temporary memory has *not* been moved to permanent memory,
631 // the AllocatePage will depend on the field of PEI_CORE_INSTANCE structure.
632 //
633 FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop);
634 FreeMemoryBottom = &(PrivateData->PhysicalMemoryBegin);
635 } else {
636 FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop);
637 FreeMemoryBottom = &(Hob.HandoffInformationTable->EfiFreeMemoryBottom);
638 }
639
640 //
641 // Check to see if on correct boundary for the memory type.
642 // If not aligned, make the allocation aligned.
643 //
644 Padding = *(FreeMemoryTop) & (Granularity - 1);
645 if ((UINTN) (*FreeMemoryTop - *FreeMemoryBottom) < Padding) {
646 DEBUG ((DEBUG_ERROR, "AllocatePages failed: Out of space after padding.\n"));
647 return EFI_OUT_OF_RESOURCES;
648 }
649
650 *(FreeMemoryTop) -= Padding;
651 if (Padding >= EFI_PAGE_SIZE) {
652 //
653 // Create a memory allocation HOB to cover
654 // the pages that we will lose to rounding
655 //
656 InternalBuildMemoryAllocationHob (
657 *(FreeMemoryTop),
658 Padding & ~(UINTN)EFI_PAGE_MASK,
659 EfiConventionalMemory
660 );
661 }
662
663 //
664 // Verify that there is sufficient memory to satisfy the allocation.
665 //
666 RemainingPages = (UINTN)(*FreeMemoryTop - *FreeMemoryBottom) >> EFI_PAGE_SHIFT;
667 //
668 // The number of remaining pages needs to be greater than or equal to that of the request pages.
669 //
670 Pages = ALIGN_VALUE (Pages, EFI_SIZE_TO_PAGES (Granularity));
671 if (RemainingPages < Pages) {
672 //
673 // Try to find free memory by searching memory allocation HOBs.
674 //
675 Status = FindFreeMemoryFromMemoryAllocationHob (MemoryType, Pages, Granularity, Memory);
676 if (!EFI_ERROR (Status)) {
677 return Status;
678 }
679 DEBUG ((EFI_D_ERROR, "AllocatePages failed: No 0x%lx Pages is available.\n", (UINT64) Pages));
680 DEBUG ((EFI_D_ERROR, "There is only left 0x%lx pages memory resource to be allocated.\n", (UINT64) RemainingPages));
681 return EFI_OUT_OF_RESOURCES;
682 } else {
683 //
684 // Update the PHIT to reflect the memory usage
685 //
686 *(FreeMemoryTop) -= Pages * EFI_PAGE_SIZE;
687
688 //
689 // Update the value for the caller
690 //
691 *Memory = *(FreeMemoryTop);
692
693 //
694 // Create a memory allocation HOB.
695 //
696 InternalBuildMemoryAllocationHob (
697 *(FreeMemoryTop),
698 Pages * EFI_PAGE_SIZE,
699 MemoryType
700 );
701
702 return EFI_SUCCESS;
703 }
704 }
705
706 /**
707 Mark the memory allocation HOB to be unused(freed) and update *FreeMemoryTop
708 if MemoryBaseAddress == *FreeMemoryTop.
709
710 @param[in] PrivateData Pointer to PeiCore's private data structure.
711 @param[in, out] MemoryAllocationHobToFree Pointer to memory allocation HOB to be freed.
712
713 **/
714 VOID
715 FreeMemoryAllocationHob (
716 IN PEI_CORE_INSTANCE *PrivateData,
717 IN OUT EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHobToFree
718 )
719 {
720 EFI_PEI_HOB_POINTERS Hob;
721 EFI_PHYSICAL_ADDRESS *FreeMemoryTop;
722 EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
723
724 Hob.Raw = PrivateData->HobList.Raw;
725
726 if (!PrivateData->PeiMemoryInstalled && PrivateData->SwitchStackSignal) {
727 //
728 // When PeiInstallMemory is called but temporary memory has *not* been moved to permanent memory,
729 // use the FreePhysicalMemoryTop field of PEI_CORE_INSTANCE structure.
730 //
731 FreeMemoryTop = &(PrivateData->FreePhysicalMemoryTop);
732 } else {
733 FreeMemoryTop = &(Hob.HandoffInformationTable->EfiFreeMemoryTop);
734 }
735
736 if (MemoryAllocationHobToFree->AllocDescriptor.MemoryBaseAddress == *FreeMemoryTop) {
737 //
738 // Update *FreeMemoryTop.
739 //
740 *FreeMemoryTop += MemoryAllocationHobToFree->AllocDescriptor.MemoryLength;
741 //
742 // Mark the memory allocation HOB to be unused(freed).
743 //
744 MemoryAllocationHobToFree->Header.HobType = EFI_HOB_TYPE_UNUSED;
745
746 MemoryAllocationHob = NULL;
747 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
748 while (Hob.Raw != NULL) {
749 if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType == EfiConventionalMemory) &&
750 (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress == *FreeMemoryTop)) {
751 //
752 // Found memory allocation HOB that has EfiConventionalMemory MemoryType and
753 // MemoryBaseAddress == new *FreeMemoryTop.
754 //
755 MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *) Hob.Raw;
756 break;
757 }
758 Hob.Raw = GET_NEXT_HOB (Hob);
759 Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw);
760 }
761 //
762 // Free memory allocation HOB iteratively.
763 //
764 if (MemoryAllocationHob != NULL) {
765 FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob);
766 }
767 }
768 }
769
770 /**
771 Frees memory pages.
772
773 @param[in] PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
774 @param[in] Memory The base physical address of the pages to be freed.
775 @param[in] Pages The number of contiguous 4 KB pages to free.
776
777 @retval EFI_SUCCESS The requested pages were freed.
778 @retval EFI_INVALID_PARAMETER Memory is not a page-aligned address or Pages is invalid.
779 @retval EFI_NOT_FOUND The requested memory pages were not allocated with
780 AllocatePages().
781
782 **/
783 EFI_STATUS
784 EFIAPI
785 PeiFreePages (
786 IN CONST EFI_PEI_SERVICES **PeiServices,
787 IN EFI_PHYSICAL_ADDRESS Memory,
788 IN UINTN Pages
789 )
790 {
791 PEI_CORE_INSTANCE *PrivateData;
792 UINT64 Bytes;
793 UINT64 Start;
794 UINT64 End;
795 EFI_PEI_HOB_POINTERS Hob;
796 EFI_HOB_MEMORY_ALLOCATION *MemoryAllocationHob;
797
798 Bytes = LShiftU64 (Pages, EFI_PAGE_SHIFT);
799 Start = Memory;
800 End = Start + Bytes - 1;
801
802 if (Pages == 0 || ((Start & EFI_PAGE_MASK) != 0) || (Start >= End)) {
803 return EFI_INVALID_PARAMETER;
804 }
805
806 PrivateData = PEI_CORE_INSTANCE_FROM_PS_THIS (PeiServices);
807 Hob.Raw = PrivateData->HobList.Raw;
808
809 if (Hob.Raw == NULL) {
810 //
811 // HOB is not initialized yet.
812 //
813 return EFI_NOT_AVAILABLE_YET;
814 }
815
816 MemoryAllocationHob = NULL;
817 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_MEMORY_ALLOCATION);
818 while (Hob.Raw != NULL) {
819 if ((Hob.MemoryAllocation->AllocDescriptor.MemoryType != EfiConventionalMemory) &&
820 (Memory >= Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress) &&
821 ((Memory + Bytes) <= (Hob.MemoryAllocation->AllocDescriptor.MemoryBaseAddress + Hob.MemoryAllocation->AllocDescriptor.MemoryLength))) {
822 //
823 // Found the memory allocation HOB that includes the memory pages to be freed.
824 //
825 MemoryAllocationHob = (EFI_HOB_MEMORY_ALLOCATION *) Hob.Raw;
826 break;
827 }
828 Hob.Raw = GET_NEXT_HOB (Hob);
829 Hob.Raw = GetNextHob (EFI_HOB_TYPE_MEMORY_ALLOCATION, Hob.Raw);
830 }
831
832 if (MemoryAllocationHob != NULL) {
833 UpdateOrSplitMemoryAllocationHob (MemoryAllocationHob, Memory, Bytes, EfiConventionalMemory);
834 FreeMemoryAllocationHob (PrivateData, MemoryAllocationHob);
835 return EFI_SUCCESS;
836 } else {
837 return EFI_NOT_FOUND;
838 }
839 }
840
841 /**
842
843 Pool allocation service. Before permanent memory is discovered, the pool will
844 be allocated in the heap in temporary memory. Generally, the size of the heap in temporary
845 memory does not exceed 64K, so the biggest pool size could be allocated is
846 64K.
847
848 @param PeiServices An indirect pointer to the EFI_PEI_SERVICES table published by the PEI Foundation.
849 @param Size Amount of memory required
850 @param Buffer Address of pointer to the buffer
851
852 @retval EFI_SUCCESS The allocation was successful
853 @retval EFI_OUT_OF_RESOURCES There is not enough heap to satisfy the requirement
854 to allocate the requested size.
855
856 **/
857 EFI_STATUS
858 EFIAPI
859 PeiAllocatePool (
860 IN CONST EFI_PEI_SERVICES **PeiServices,
861 IN UINTN Size,
862 OUT VOID **Buffer
863 )
864 {
865 EFI_STATUS Status;
866 EFI_HOB_MEMORY_POOL *Hob;
867
868 //
869 // If some "post-memory" PEIM wishes to allocate larger pool,
870 // it should use AllocatePages service instead.
871 //
872
873 //
874 // Generally, the size of heap in temporary memory does not exceed 64K,
875 // HobLength is multiples of 8 bytes, so the maximum size of pool is 0xFFF8 - sizeof (EFI_HOB_MEMORY_POOL)
876 //
877 if (Size > (0xFFF8 - sizeof (EFI_HOB_MEMORY_POOL))) {
878 return EFI_OUT_OF_RESOURCES;
879 }
880
881 Status = PeiServicesCreateHob (
882 EFI_HOB_TYPE_MEMORY_POOL,
883 (UINT16)(sizeof (EFI_HOB_MEMORY_POOL) + Size),
884 (VOID **)&Hob
885 );
886 ASSERT_EFI_ERROR (Status);
887
888 if (EFI_ERROR (Status)) {
889 *Buffer = NULL;
890 } else {
891 *Buffer = Hob + 1;
892 }
893
894 return Status;
895 }