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