]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Universal/CapsulePei/UefiCapsule.c
MdeModulePkg/Ip4Dxe: Clean up IP4 interface if failed to open ARP protocol.
[mirror_edk2.git] / MdeModulePkg / Universal / CapsulePei / UefiCapsule.c
... / ...
CommitLineData
1/** @file\r
2 Capsule update PEIM for UEFI2.0\r
3\r
4Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>\r
5Copyright (c) 2017, AMD Incorporated. All rights reserved.<BR>\r
6\r
7This program and the accompanying materials\r
8are licensed and made available under the terms and conditions\r
9of the BSD License which accompanies this distribution. The\r
10full text of the license may be found at\r
11http://opensource.org/licenses/bsd-license.php\r
12\r
13THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
14WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
15\r
16**/\r
17\r
18#include "Capsule.h"\r
19\r
20#ifdef MDE_CPU_IA32\r
21//\r
22// Global Descriptor Table (GDT)\r
23//\r
24GLOBAL_REMOVE_IF_UNREFERENCED IA32_SEGMENT_DESCRIPTOR mGdtEntries[] = {\r
25/* selector { Global Segment Descriptor } */\r
26/* 0x00 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //null descriptor\r
27/* 0x08 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear data segment descriptor\r
28/* 0x10 */ {{0xffff, 0, 0, 0xf, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //linear code segment descriptor\r
29/* 0x18 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor\r
30/* 0x20 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system code segment descriptor\r
31/* 0x28 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor\r
32/* 0x30 */ {{0xffff, 0, 0, 0x3, 1, 0, 1, 0xf, 0, 0, 1, 1, 0}}, //system data segment descriptor\r
33/* 0x38 */ {{0xffff, 0, 0, 0xb, 1, 0, 1, 0xf, 0, 1, 0, 1, 0}}, //system code segment descriptor\r
34/* 0x40 */ {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, //spare segment descriptor\r
35};\r
36\r
37//\r
38// IA32 Gdt register\r
39//\r
40GLOBAL_REMOVE_IF_UNREFERENCED CONST IA32_DESCRIPTOR mGdt = {\r
41 sizeof (mGdtEntries) - 1,\r
42 (UINTN) mGdtEntries\r
43 };\r
44\r
45\r
46/**\r
47 The function will check if 1G page is supported.\r
48\r
49 @retval TRUE 1G page is supported.\r
50 @retval FALSE 1G page is not supported.\r
51\r
52**/\r
53BOOLEAN\r
54IsPage1GSupport (\r
55 VOID\r
56 )\r
57{\r
58 UINT32 RegEax;\r
59 UINT32 RegEdx;\r
60 BOOLEAN Page1GSupport;\r
61\r
62 Page1GSupport = FALSE;\r
63 if (PcdGetBool(PcdUse1GPageTable)) {\r
64 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
65 if (RegEax >= 0x80000001) {\r
66 AsmCpuid (0x80000001, NULL, NULL, NULL, &RegEdx);\r
67 if ((RegEdx & BIT26) != 0) {\r
68 Page1GSupport = TRUE;\r
69 }\r
70 }\r
71 }\r
72\r
73 return Page1GSupport;\r
74}\r
75\r
76/**\r
77 Calculate the total size of page table.\r
78\r
79 @param[in] Page1GSupport 1G page support or not.\r
80\r
81 @return The size of page table.\r
82\r
83**/\r
84UINTN\r
85CalculatePageTableSize (\r
86 IN BOOLEAN Page1GSupport\r
87 )\r
88{\r
89 UINTN ExtraPageTablePages;\r
90 UINTN TotalPagesNum;\r
91 UINT8 PhysicalAddressBits;\r
92 UINT32 NumberOfPml4EntriesNeeded;\r
93 UINT32 NumberOfPdpEntriesNeeded;\r
94\r
95 //\r
96 // Create 4G page table by default,\r
97 // and let PF handler to handle > 4G request.\r
98 //\r
99 PhysicalAddressBits = 32;\r
100 ExtraPageTablePages = EXTRA_PAGE_TABLE_PAGES;\r
101\r
102 //\r
103 // Calculate the table entries needed.\r
104 //\r
105 if (PhysicalAddressBits <= 39 ) {\r
106 NumberOfPml4EntriesNeeded = 1;\r
107 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));\r
108 } else {\r
109 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));\r
110 NumberOfPdpEntriesNeeded = 512;\r
111 }\r
112\r
113 if (!Page1GSupport) {\r
114 TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1;\r
115 } else {\r
116 TotalPagesNum = NumberOfPml4EntriesNeeded + 1;\r
117 }\r
118 TotalPagesNum += ExtraPageTablePages;\r
119\r
120 return EFI_PAGES_TO_SIZE (TotalPagesNum);\r
121}\r
122\r
123/**\r
124 Allocates and fills in the Page Directory and Page Table Entries to\r
125 establish a 4G page table.\r
126\r
127 @param[in] PageTablesAddress The base address of page table.\r
128 @param[in] Page1GSupport 1G page support or not.\r
129\r
130**/\r
131VOID\r
132Create4GPageTables (\r
133 IN EFI_PHYSICAL_ADDRESS PageTablesAddress,\r
134 IN BOOLEAN Page1GSupport\r
135 )\r
136{ \r
137 UINT8 PhysicalAddressBits;\r
138 EFI_PHYSICAL_ADDRESS PageAddress;\r
139 UINTN IndexOfPml4Entries;\r
140 UINTN IndexOfPdpEntries;\r
141 UINTN IndexOfPageDirectoryEntries;\r
142 UINT32 NumberOfPml4EntriesNeeded;\r
143 UINT32 NumberOfPdpEntriesNeeded;\r
144 PAGE_MAP_AND_DIRECTORY_POINTER *PageMapLevel4Entry;\r
145 PAGE_MAP_AND_DIRECTORY_POINTER *PageMap;\r
146 PAGE_MAP_AND_DIRECTORY_POINTER *PageDirectoryPointerEntry;\r
147 PAGE_TABLE_ENTRY *PageDirectoryEntry;\r
148 UINTN BigPageAddress;\r
149 PAGE_TABLE_1G_ENTRY *PageDirectory1GEntry;\r
150 UINT64 AddressEncMask;\r
151\r
152 //\r
153 // Make sure AddressEncMask is contained to smallest supported address field.\r
154 //\r
155 AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
156\r
157 //\r
158 // Create 4G page table by default,\r
159 // and let PF handler to handle > 4G request.\r
160 //\r
161 PhysicalAddressBits = 32;\r
162\r
163 //\r
164 // Calculate the table entries needed.\r
165 //\r
166 if (PhysicalAddressBits <= 39 ) {\r
167 NumberOfPml4EntriesNeeded = 1;\r
168 NumberOfPdpEntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 30));\r
169 } else {\r
170 NumberOfPml4EntriesNeeded = (UINT32)LShiftU64 (1, (PhysicalAddressBits - 39));\r
171 NumberOfPdpEntriesNeeded = 512;\r
172 }\r
173\r
174 //\r
175 // Pre-allocate big pages to avoid later allocations. \r
176 //\r
177 BigPageAddress = (UINTN) PageTablesAddress;\r
178\r
179 //\r
180 // By architecture only one PageMapLevel4 exists - so lets allocate storage for it.\r
181 //\r
182 PageMap = (VOID *) BigPageAddress;\r
183 BigPageAddress += SIZE_4KB;\r
184\r
185 PageMapLevel4Entry = PageMap;\r
186 PageAddress = 0;\r
187 for (IndexOfPml4Entries = 0; IndexOfPml4Entries < NumberOfPml4EntriesNeeded; IndexOfPml4Entries++, PageMapLevel4Entry++) {\r
188 //\r
189 // Each PML4 entry points to a page of Page Directory Pointer entires.\r
190 // So lets allocate space for them and fill them in in the IndexOfPdpEntries loop.\r
191 //\r
192 PageDirectoryPointerEntry = (VOID *) BigPageAddress;\r
193 BigPageAddress += SIZE_4KB;\r
194\r
195 //\r
196 // Make a PML4 Entry\r
197 //\r
198 PageMapLevel4Entry->Uint64 = (UINT64)(UINTN)PageDirectoryPointerEntry | AddressEncMask;\r
199 PageMapLevel4Entry->Bits.ReadWrite = 1;\r
200 PageMapLevel4Entry->Bits.Present = 1;\r
201\r
202 if (Page1GSupport) {\r
203 PageDirectory1GEntry = (VOID *) PageDirectoryPointerEntry;\r
204 \r
205 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectory1GEntry++, PageAddress += SIZE_1GB) {\r
206 //\r
207 // Fill in the Page Directory entries\r
208 //\r
209 PageDirectory1GEntry->Uint64 = (UINT64)PageAddress | AddressEncMask;\r
210 PageDirectory1GEntry->Bits.ReadWrite = 1;\r
211 PageDirectory1GEntry->Bits.Present = 1;\r
212 PageDirectory1GEntry->Bits.MustBe1 = 1;\r
213 }\r
214 } else {\r
215 for (IndexOfPdpEntries = 0; IndexOfPdpEntries < NumberOfPdpEntriesNeeded; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {\r
216 //\r
217 // Each Directory Pointer entries points to a page of Page Directory entires.\r
218 // So allocate space for them and fill them in in the IndexOfPageDirectoryEntries loop.\r
219 // \r
220 PageDirectoryEntry = (VOID *) BigPageAddress;\r
221 BigPageAddress += SIZE_4KB;\r
222\r
223 //\r
224 // Fill in a Page Directory Pointer Entries\r
225 //\r
226 PageDirectoryPointerEntry->Uint64 = (UINT64)(UINTN)PageDirectoryEntry | AddressEncMask;\r
227 PageDirectoryPointerEntry->Bits.ReadWrite = 1;\r
228 PageDirectoryPointerEntry->Bits.Present = 1;\r
229\r
230 for (IndexOfPageDirectoryEntries = 0; IndexOfPageDirectoryEntries < 512; IndexOfPageDirectoryEntries++, PageDirectoryEntry++, PageAddress += SIZE_2MB) {\r
231 //\r
232 // Fill in the Page Directory entries\r
233 //\r
234 PageDirectoryEntry->Uint64 = (UINT64)PageAddress | AddressEncMask;\r
235 PageDirectoryEntry->Bits.ReadWrite = 1;\r
236 PageDirectoryEntry->Bits.Present = 1;\r
237 PageDirectoryEntry->Bits.MustBe1 = 1;\r
238 }\r
239 }\r
240\r
241 for (; IndexOfPdpEntries < 512; IndexOfPdpEntries++, PageDirectoryPointerEntry++) {\r
242 ZeroMem (\r
243 PageDirectoryPointerEntry,\r
244 sizeof(PAGE_MAP_AND_DIRECTORY_POINTER)\r
245 );\r
246 }\r
247 }\r
248 }\r
249\r
250 //\r
251 // For the PML4 entries we are not using fill in a null entry.\r
252 //\r
253 for (; IndexOfPml4Entries < 512; IndexOfPml4Entries++, PageMapLevel4Entry++) {\r
254 ZeroMem (\r
255 PageMapLevel4Entry,\r
256 sizeof (PAGE_MAP_AND_DIRECTORY_POINTER)\r
257 );\r
258 }\r
259}\r
260\r
261/**\r
262 Return function from long mode to 32-bit mode.\r
263\r
264 @param EntrypointContext Context for mode switching\r
265 @param ReturnContext Context for mode switching\r
266\r
267**/\r
268VOID\r
269ReturnFunction (\r
270 SWITCH_32_TO_64_CONTEXT *EntrypointContext,\r
271 SWITCH_64_TO_32_CONTEXT *ReturnContext\r
272 )\r
273{\r
274 //\r
275 // Restore original GDT\r
276 //\r
277 AsmWriteGdtr (&ReturnContext->Gdtr);\r
278\r
279 //\r
280 // return to original caller\r
281 //\r
282 LongJump ((BASE_LIBRARY_JUMP_BUFFER *)(UINTN)EntrypointContext->JumpBuffer, 1);\r
283\r
284 //\r
285 // never be here\r
286 //\r
287 ASSERT (FALSE);\r
288}\r
289\r
290/**\r
291 Thunk function from 32-bit protection mode to long mode.\r
292\r
293 @param PageTableAddress Page table base address\r
294 @param Context Context for mode switching\r
295 @param ReturnContext Context for mode switching\r
296\r
297 @retval EFI_SUCCESS Function successfully executed.\r
298\r
299**/\r
300EFI_STATUS\r
301Thunk32To64 (\r
302 EFI_PHYSICAL_ADDRESS PageTableAddress,\r
303 SWITCH_32_TO_64_CONTEXT *Context,\r
304 SWITCH_64_TO_32_CONTEXT *ReturnContext\r
305 )\r
306{\r
307 UINTN SetJumpFlag;\r
308 EFI_STATUS Status;\r
309\r
310 //\r
311 // Save return address, LongJump will return here then\r
312 //\r
313 SetJumpFlag = SetJump ((BASE_LIBRARY_JUMP_BUFFER *) (UINTN) Context->JumpBuffer);\r
314\r
315 if (SetJumpFlag == 0) {\r
316\r
317 //\r
318 // Build 4G Page Tables.\r
319 //\r
320 Create4GPageTables (PageTableAddress, Context->Page1GSupport);\r
321\r
322 //\r
323 // Create 64-bit GDT\r
324 //\r
325 AsmWriteGdtr (&mGdt);\r
326\r
327 //\r
328 // Write CR3\r
329 //\r
330 AsmWriteCr3 ((UINTN) PageTableAddress);\r
331\r
332 DEBUG ((\r
333 DEBUG_INFO,\r
334 "%a() Stack Base: 0x%lx, Stack Size: 0x%lx\n",\r
335 __FUNCTION__,\r
336 Context->StackBufferBase,\r
337 Context->StackBufferLength\r
338 ));\r
339\r
340 //\r
341 // Disable interrupt of Debug timer, since the IDT table cannot work in long mode\r
342 //\r
343 SaveAndSetDebugTimerInterrupt (FALSE);\r
344 //\r
345 // Transfer to long mode\r
346 //\r
347 AsmEnablePaging64 (\r
348 0x38,\r
349 (UINT64) Context->EntryPoint,\r
350 (UINT64)(UINTN) Context,\r
351 (UINT64)(UINTN) ReturnContext,\r
352 Context->StackBufferBase + Context->StackBufferLength\r
353 );\r
354 }\r
355\r
356 //\r
357 // Convert to 32-bit Status and return\r
358 //\r
359 Status = EFI_SUCCESS;\r
360 if ((UINTN) ReturnContext->ReturnStatus != 0) {\r
361 Status = ENCODE_ERROR ((UINTN) ReturnContext->ReturnStatus);\r
362 }\r
363 \r
364 return Status;\r
365}\r
366\r
367/**\r
368 If in 32 bit protection mode, and coalesce image is of X64, switch to long mode.\r
369\r
370 @param LongModeBuffer The context of long mode.\r
371 @param CoalesceEntry Entry of coalesce image.\r
372 @param BlockListAddr Address of block list.\r
373 @param MemoryResource Pointer to the buffer of memory resource descriptor.\r
374 @param MemoryBase Base of memory range.\r
375 @param MemorySize Size of memory range.\r
376\r
377 @retval EFI_SUCCESS Successfully switched to long mode and execute coalesce.\r
378 @retval Others Failed to execute coalesce in long mode.\r
379\r
380**/\r
381EFI_STATUS\r
382ModeSwitch (\r
383 IN EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer,\r
384 IN COALESCE_ENTRY CoalesceEntry,\r
385 IN EFI_PHYSICAL_ADDRESS BlockListAddr,\r
386 IN MEMORY_RESOURCE_DESCRIPTOR *MemoryResource,\r
387 IN OUT VOID **MemoryBase,\r
388 IN OUT UINTN *MemorySize\r
389 )\r
390{\r
391 EFI_STATUS Status;\r
392 EFI_PHYSICAL_ADDRESS MemoryBase64;\r
393 UINT64 MemorySize64;\r
394 EFI_PHYSICAL_ADDRESS MemoryEnd64;\r
395 SWITCH_32_TO_64_CONTEXT Context;\r
396 SWITCH_64_TO_32_CONTEXT ReturnContext;\r
397 BASE_LIBRARY_JUMP_BUFFER JumpBuffer;\r
398 EFI_PHYSICAL_ADDRESS ReservedRangeBase;\r
399 EFI_PHYSICAL_ADDRESS ReservedRangeEnd;\r
400 BOOLEAN Page1GSupport;\r
401\r
402 ZeroMem (&Context, sizeof (SWITCH_32_TO_64_CONTEXT));\r
403 ZeroMem (&ReturnContext, sizeof (SWITCH_64_TO_32_CONTEXT));\r
404 \r
405 MemoryBase64 = (UINT64) (UINTN) *MemoryBase;\r
406 MemorySize64 = (UINT64) (UINTN) *MemorySize;\r
407 MemoryEnd64 = MemoryBase64 + MemorySize64;\r
408\r
409 Page1GSupport = IsPage1GSupport ();\r
410\r
411 //\r
412 // Merge memory range reserved for stack and page table \r
413 //\r
414 if (LongModeBuffer->StackBaseAddress < LongModeBuffer->PageTableAddress) {\r
415 ReservedRangeBase = LongModeBuffer->StackBaseAddress;\r
416 ReservedRangeEnd = LongModeBuffer->PageTableAddress + CalculatePageTableSize (Page1GSupport);\r
417 } else {\r
418 ReservedRangeBase = LongModeBuffer->PageTableAddress;\r
419 ReservedRangeEnd = LongModeBuffer->StackBaseAddress + LongModeBuffer->StackSize;\r
420 }\r
421\r
422 //\r
423 // Check if memory range reserved is overlap with MemoryBase ~ MemoryBase + MemorySize.\r
424 // If they are overlapped, get a larger range to process capsule data.\r
425 //\r
426 if (ReservedRangeBase <= MemoryBase64) {\r
427 if (ReservedRangeEnd < MemoryEnd64) {\r
428 MemoryBase64 = ReservedRangeEnd;\r
429 } else {\r
430 DEBUG ((EFI_D_ERROR, "Memory is not enough to process capsule!\n"));\r
431 return EFI_OUT_OF_RESOURCES;\r
432 }\r
433 } else if (ReservedRangeBase < MemoryEnd64) {\r
434 if (ReservedRangeEnd < MemoryEnd64 &&\r
435 ReservedRangeBase - MemoryBase64 < MemoryEnd64 - ReservedRangeEnd) {\r
436 MemoryBase64 = ReservedRangeEnd;\r
437 } else {\r
438 MemorySize64 = (UINT64)(UINTN)(ReservedRangeBase - MemoryBase64);\r
439 }\r
440 }\r
441\r
442 //\r
443 // Initialize context jumping to 64-bit enviroment\r
444 //\r
445 Context.JumpBuffer = (EFI_PHYSICAL_ADDRESS)(UINTN)&JumpBuffer;\r
446 Context.StackBufferBase = LongModeBuffer->StackBaseAddress;\r
447 Context.StackBufferLength = LongModeBuffer->StackSize;\r
448 Context.EntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)CoalesceEntry;\r
449 Context.BlockListAddr = BlockListAddr;\r
450 Context.MemoryResource = (EFI_PHYSICAL_ADDRESS)(UINTN)MemoryResource;\r
451 Context.MemoryBase64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemoryBase64;\r
452 Context.MemorySize64Ptr = (EFI_PHYSICAL_ADDRESS)(UINTN)&MemorySize64;\r
453 Context.Page1GSupport = Page1GSupport;\r
454 Context.AddressEncMask = PcdGet64 (PcdPteMemoryEncryptionAddressOrMask) & PAGING_1G_ADDRESS_MASK_64;\r
455\r
456 //\r
457 // Prepare data for return back\r
458 //\r
459 ReturnContext.ReturnCs = 0x10;\r
460 ReturnContext.ReturnEntryPoint = (EFI_PHYSICAL_ADDRESS)(UINTN)ReturnFunction;\r
461 //\r
462 // Will save the return status of processing capsule\r
463 //\r
464 ReturnContext.ReturnStatus = 0;\r
465 \r
466 //\r
467 // Save original GDT\r
468 //\r
469 AsmReadGdtr ((IA32_DESCRIPTOR *)&ReturnContext.Gdtr);\r
470 \r
471 Status = Thunk32To64 (LongModeBuffer->PageTableAddress, &Context, &ReturnContext);\r
472 \r
473 if (!EFI_ERROR (Status)) {\r
474 *MemoryBase = (VOID *) (UINTN) MemoryBase64;\r
475 *MemorySize = (UINTN) MemorySize64;\r
476 }\r
477\r
478 return Status;\r
479\r
480}\r
481\r
482/**\r
483 Locates the coalesce image entry point, and detects its machine type.\r
484\r
485 @param CoalesceImageEntryPoint Pointer to coalesce image entry point for output.\r
486 @param CoalesceImageMachineType Pointer to machine type of coalesce image.\r
487\r
488 @retval EFI_SUCCESS Coalesce image successfully located.\r
489 @retval Others Failed to locate the coalesce image.\r
490\r
491**/\r
492EFI_STATUS\r
493FindCapsuleCoalesceImage (\r
494 OUT EFI_PHYSICAL_ADDRESS *CoalesceImageEntryPoint,\r
495 OUT UINT16 *CoalesceImageMachineType\r
496 )\r
497{\r
498 EFI_STATUS Status;\r
499 UINTN Instance;\r
500 EFI_PEI_LOAD_FILE_PPI *LoadFile;\r
501 EFI_PEI_FV_HANDLE VolumeHandle;\r
502 EFI_PEI_FILE_HANDLE FileHandle;\r
503 EFI_PHYSICAL_ADDRESS CoalesceImageAddress;\r
504 UINT64 CoalesceImageSize;\r
505 UINT32 AuthenticationState;\r
506\r
507 Instance = 0;\r
508\r
509 while (TRUE) {\r
510 Status = PeiServicesFfsFindNextVolume (Instance++, &VolumeHandle);\r
511 if (EFI_ERROR (Status)) {\r
512 return Status;\r
513 }\r
514 Status = PeiServicesFfsFindFileByName (PcdGetPtr(PcdCapsuleCoalesceFile), VolumeHandle, &FileHandle);\r
515 if (!EFI_ERROR (Status)) {\r
516 Status = PeiServicesLocatePpi (&gEfiPeiLoadFilePpiGuid, 0, NULL, (VOID **) &LoadFile);\r
517 ASSERT_EFI_ERROR (Status);\r
518\r
519 Status = LoadFile->LoadFile (\r
520 LoadFile,\r
521 FileHandle,\r
522 &CoalesceImageAddress,\r
523 &CoalesceImageSize,\r
524 CoalesceImageEntryPoint,\r
525 &AuthenticationState\r
526 );\r
527 if (EFI_ERROR (Status)) {\r
528 DEBUG ((EFI_D_ERROR, "Unable to find PE32 section in CapsuleX64 image ffs %r!\n", Status));\r
529 return Status;\r
530 }\r
531 *CoalesceImageMachineType = PeCoffLoaderGetMachineType ((VOID *) (UINTN) CoalesceImageAddress);\r
532 break;\r
533 } else {\r
534 continue;\r
535 }\r
536 }\r
537\r
538 return Status;\r
539}\r
540\r
541/**\r
542 Gets the reserved long mode buffer.\r
543\r
544 @param LongModeBuffer Pointer to the long mode buffer for output.\r
545\r
546 @retval EFI_SUCCESS Long mode buffer successfully retrieved.\r
547 @retval Others Variable storing long mode buffer not found.\r
548\r
549**/\r
550EFI_STATUS\r
551GetLongModeContext (\r
552 OUT EFI_CAPSULE_LONG_MODE_BUFFER *LongModeBuffer\r
553 )\r
554{\r
555 EFI_STATUS Status;\r
556 UINTN Size;\r
557 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;\r
558\r
559 Status = PeiServicesLocatePpi (\r
560 &gEfiPeiReadOnlyVariable2PpiGuid,\r
561 0,\r
562 NULL,\r
563 (VOID **) &PPIVariableServices\r
564 );\r
565 ASSERT_EFI_ERROR (Status);\r
566\r
567 Size = sizeof (EFI_CAPSULE_LONG_MODE_BUFFER);\r
568 Status = PPIVariableServices->GetVariable (\r
569 PPIVariableServices,\r
570 EFI_CAPSULE_LONG_MODE_BUFFER_NAME,\r
571 &gEfiCapsuleVendorGuid,\r
572 NULL,\r
573 &Size,\r
574 LongModeBuffer\r
575 );\r
576 if (EFI_ERROR (Status)) {\r
577 DEBUG (( EFI_D_ERROR, "Error Get LongModeBuffer variable %r!\n", Status));\r
578 }\r
579 return Status;\r
580}\r
581#endif\r
582\r
583#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)\r
584/**\r
585 Get physical address bits.\r
586\r
587 @return Physical address bits.\r
588\r
589**/\r
590UINT8\r
591GetPhysicalAddressBits (\r
592 VOID\r
593 )\r
594{\r
595 UINT32 RegEax;\r
596 UINT8 PhysicalAddressBits;\r
597 VOID *Hob;\r
598\r
599 //\r
600 // Get physical address bits supported.\r
601 //\r
602 Hob = GetFirstHob (EFI_HOB_TYPE_CPU);\r
603 if (Hob != NULL) {\r
604 PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace;\r
605 } else {\r
606 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL);\r
607 if (RegEax >= 0x80000008) {\r
608 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL);\r
609 PhysicalAddressBits = (UINT8) RegEax;\r
610 } else {\r
611 PhysicalAddressBits = 36;\r
612 }\r
613 }\r
614\r
615 //\r
616 // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses.\r
617 //\r
618 ASSERT (PhysicalAddressBits <= 52);\r
619 if (PhysicalAddressBits > 48) {\r
620 PhysicalAddressBits = 48;\r
621 }\r
622\r
623 return PhysicalAddressBits;\r
624}\r
625#endif\r
626\r
627/**\r
628 Sort memory resource entries based upon PhysicalStart, from low to high.\r
629\r
630 @param[in, out] MemoryResource A pointer to the memory resource entry buffer.\r
631\r
632**/\r
633VOID\r
634SortMemoryResourceDescriptor (\r
635 IN OUT MEMORY_RESOURCE_DESCRIPTOR *MemoryResource\r
636 )\r
637{\r
638 MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEntry;\r
639 MEMORY_RESOURCE_DESCRIPTOR *NextMemoryResourceEntry;\r
640 MEMORY_RESOURCE_DESCRIPTOR TempMemoryResource;\r
641\r
642 MemoryResourceEntry = MemoryResource;\r
643 NextMemoryResourceEntry = MemoryResource + 1;\r
644 while (MemoryResourceEntry->ResourceLength != 0) {\r
645 while (NextMemoryResourceEntry->ResourceLength != 0) {\r
646 if (MemoryResourceEntry->PhysicalStart > NextMemoryResourceEntry->PhysicalStart) {\r
647 CopyMem (&TempMemoryResource, MemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR));\r
648 CopyMem (MemoryResourceEntry, NextMemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR));\r
649 CopyMem (NextMemoryResourceEntry, &TempMemoryResource, sizeof (MEMORY_RESOURCE_DESCRIPTOR));\r
650 }\r
651\r
652 NextMemoryResourceEntry = NextMemoryResourceEntry + 1;\r
653 }\r
654\r
655 MemoryResourceEntry = MemoryResourceEntry + 1;\r
656 NextMemoryResourceEntry = MemoryResourceEntry + 1;\r
657 }\r
658}\r
659\r
660/**\r
661 Merge continous memory resource entries.\r
662\r
663 @param[in, out] MemoryResource A pointer to the memory resource entry buffer.\r
664\r
665**/\r
666VOID\r
667MergeMemoryResourceDescriptor (\r
668 IN OUT MEMORY_RESOURCE_DESCRIPTOR *MemoryResource\r
669 )\r
670{\r
671 MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEntry;\r
672 MEMORY_RESOURCE_DESCRIPTOR *NewMemoryResourceEntry;\r
673 MEMORY_RESOURCE_DESCRIPTOR *NextMemoryResourceEntry;\r
674 MEMORY_RESOURCE_DESCRIPTOR *MemoryResourceEnd;\r
675\r
676 MemoryResourceEntry = MemoryResource;\r
677 NewMemoryResourceEntry = MemoryResource;\r
678 while (MemoryResourceEntry->ResourceLength != 0) {\r
679 CopyMem (NewMemoryResourceEntry, MemoryResourceEntry, sizeof (MEMORY_RESOURCE_DESCRIPTOR));\r
680 NextMemoryResourceEntry = MemoryResourceEntry + 1;\r
681\r
682 while ((NextMemoryResourceEntry->ResourceLength != 0) &&\r
683 (NextMemoryResourceEntry->PhysicalStart == (MemoryResourceEntry->PhysicalStart + MemoryResourceEntry->ResourceLength))) {\r
684 MemoryResourceEntry->ResourceLength += NextMemoryResourceEntry->ResourceLength;\r
685 if (NewMemoryResourceEntry != MemoryResourceEntry) {\r
686 NewMemoryResourceEntry->ResourceLength += NextMemoryResourceEntry->ResourceLength;\r
687 }\r
688 \r
689 NextMemoryResourceEntry = NextMemoryResourceEntry + 1;\r
690 }\r
691\r
692 MemoryResourceEntry = NextMemoryResourceEntry;\r
693 NewMemoryResourceEntry = NewMemoryResourceEntry + 1;\r
694 }\r
695\r
696 //\r
697 // Set NULL terminate memory resource descriptor after merging.\r
698 //\r
699 MemoryResourceEnd = NewMemoryResourceEntry;\r
700 ZeroMem (MemoryResourceEnd, sizeof (MEMORY_RESOURCE_DESCRIPTOR));\r
701}\r
702\r
703/**\r
704 Build memory resource descriptor from resource descriptor in HOB list.\r
705\r
706 @return Pointer to the buffer of memory resource descriptor.\r
707 NULL if no memory resource descriptor reported in HOB list\r
708 before capsule Coalesce.\r
709\r
710**/\r
711MEMORY_RESOURCE_DESCRIPTOR *\r
712BuildMemoryResourceDescriptor (\r
713 VOID\r
714 )\r
715{\r
716 EFI_PEI_HOB_POINTERS Hob;\r
717 UINTN Index;\r
718 EFI_HOB_RESOURCE_DESCRIPTOR *ResourceDescriptor;\r
719 MEMORY_RESOURCE_DESCRIPTOR *MemoryResource;\r
720 EFI_STATUS Status;\r
721\r
722 //\r
723 // Get the count of memory resource descriptor.\r
724 //\r
725 Index = 0;\r
726 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);\r
727 while (Hob.Raw != NULL) {\r
728 ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw;\r
729 if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {\r
730 Index++;\r
731 }\r
732 Hob.Raw = GET_NEXT_HOB (Hob);\r
733 Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);\r
734 }\r
735\r
736 if (Index == 0) {\r
737 DEBUG ((EFI_D_INFO | EFI_D_WARN, "No memory resource descriptor reported in HOB list before capsule Coalesce\n"));\r
738#if defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)\r
739 //\r
740 // Allocate memory to hold memory resource descriptor,\r
741 // include extra one NULL terminate memory resource descriptor.\r
742 //\r
743 Status = PeiServicesAllocatePool ((1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource);\r
744 ASSERT_EFI_ERROR (Status);\r
745 ZeroMem (MemoryResource, (1 + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR));\r
746 \r
747 MemoryResource[0].PhysicalStart = 0;\r
748 MemoryResource[0].ResourceLength = LShiftU64 (1, GetPhysicalAddressBits ());\r
749 DEBUG ((EFI_D_INFO, "MemoryResource[0x0] - Start(0x%0lx) Length(0x%0lx)\n",\r
750 MemoryResource[0x0].PhysicalStart, MemoryResource[0x0].ResourceLength));\r
751 return MemoryResource;\r
752#else\r
753 return NULL;\r
754#endif\r
755 }\r
756\r
757 //\r
758 // Allocate memory to hold memory resource descriptor,\r
759 // include extra one NULL terminate memory resource descriptor.\r
760 //\r
761 Status = PeiServicesAllocatePool ((Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR), (VOID **) &MemoryResource);\r
762 ASSERT_EFI_ERROR (Status);\r
763 ZeroMem (MemoryResource, (Index + 1) * sizeof (MEMORY_RESOURCE_DESCRIPTOR));\r
764\r
765 //\r
766 // Get the content of memory resource descriptor.\r
767 //\r
768 Index = 0;\r
769 Hob.Raw = GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR);\r
770 while (Hob.Raw != NULL) {\r
771 ResourceDescriptor = (EFI_HOB_RESOURCE_DESCRIPTOR *) Hob.Raw;\r
772 if (ResourceDescriptor->ResourceType == EFI_RESOURCE_SYSTEM_MEMORY) {\r
773 DEBUG ((EFI_D_INFO, "MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",\r
774 Index, ResourceDescriptor->PhysicalStart, ResourceDescriptor->ResourceLength));\r
775 MemoryResource[Index].PhysicalStart = ResourceDescriptor->PhysicalStart;\r
776 MemoryResource[Index].ResourceLength = ResourceDescriptor->ResourceLength;\r
777 Index++;\r
778 }\r
779 Hob.Raw = GET_NEXT_HOB (Hob);\r
780 Hob.Raw = GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, Hob.Raw);\r
781 }\r
782\r
783 SortMemoryResourceDescriptor (MemoryResource);\r
784 MergeMemoryResourceDescriptor (MemoryResource);\r
785\r
786 DEBUG ((DEBUG_INFO, "Dump MemoryResource[] after sorted and merged\n"));\r
787 for (Index = 0; MemoryResource[Index].ResourceLength != 0; Index++) {\r
788 DEBUG ((\r
789 DEBUG_INFO,\r
790 " MemoryResource[0x%x] - Start(0x%0lx) Length(0x%0lx)\n",\r
791 Index,\r
792 MemoryResource[Index].PhysicalStart,\r
793 MemoryResource[Index].ResourceLength\r
794 ));\r
795 }\r
796\r
797 return MemoryResource;\r
798}\r
799\r
800/**\r
801 Checks for the presence of capsule descriptors.\r
802 Get capsule descriptors from variable CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...\r
803 and save to DescriptorBuffer.\r
804\r
805 @param DescriptorBuffer Pointer to the capsule descriptors\r
806\r
807 @retval EFI_SUCCESS a valid capsule is present\r
808 @retval EFI_NOT_FOUND if a valid capsule is not present\r
809**/\r
810EFI_STATUS\r
811GetCapsuleDescriptors (\r
812 IN EFI_PHYSICAL_ADDRESS *DescriptorBuffer\r
813 )\r
814{\r
815 EFI_STATUS Status;\r
816 UINTN Size;\r
817 UINTN Index;\r
818 UINTN TempIndex;\r
819 UINTN ValidIndex;\r
820 BOOLEAN Flag;\r
821 CHAR16 CapsuleVarName[30];\r
822 CHAR16 *TempVarName;\r
823 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64;\r
824 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;\r
825\r
826 Index = 0;\r
827 TempVarName = NULL;\r
828 CapsuleVarName[0] = 0;\r
829 ValidIndex = 0;\r
830 CapsuleDataPtr64 = 0;\r
831 \r
832 Status = PeiServicesLocatePpi (\r
833 &gEfiPeiReadOnlyVariable2PpiGuid,\r
834 0,\r
835 NULL,\r
836 (VOID **) &PPIVariableServices\r
837 );\r
838 if (Status == EFI_SUCCESS) {\r
839 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);\r
840 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);\r
841 Size = sizeof (CapsuleDataPtr64);\r
842 while (1) {\r
843 if (Index == 0) {\r
844 //\r
845 // For the first Capsule Image\r
846 //\r
847 Status = PPIVariableServices->GetVariable (\r
848 PPIVariableServices,\r
849 CapsuleVarName,\r
850 &gEfiCapsuleVendorGuid,\r
851 NULL,\r
852 &Size,\r
853 (VOID *) &CapsuleDataPtr64\r
854 );\r
855 if (EFI_ERROR (Status)) {\r
856 DEBUG ((DEBUG_INFO, "Capsule -- capsule variable not set\n"));\r
857 return EFI_NOT_FOUND;\r
858 }\r
859 //\r
860 // We have a chicken/egg situation where the memory init code needs to\r
861 // know the boot mode prior to initializing memory. For this case, our\r
862 // validate function will fail. We can detect if this is the case if blocklist\r
863 // pointer is null. In that case, return success since we know that the\r
864 // variable is set.\r
865 //\r
866 if (DescriptorBuffer == NULL) {\r
867 return EFI_SUCCESS;\r
868 }\r
869 } else {\r
870 UnicodeValueToStringS (\r
871 TempVarName,\r
872 sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),\r
873 0,\r
874 Index,\r
875 0\r
876 );\r
877 Status = PPIVariableServices->GetVariable (\r
878 PPIVariableServices,\r
879 CapsuleVarName,\r
880 &gEfiCapsuleVendorGuid,\r
881 NULL,\r
882 &Size,\r
883 (VOID *) &CapsuleDataPtr64\r
884 );\r
885 if (EFI_ERROR (Status)) {\r
886 break;\r
887 }\r
888 \r
889 //\r
890 // If this BlockList has been linked before, skip this variable\r
891 //\r
892 Flag = FALSE;\r
893 for (TempIndex = 0; TempIndex < ValidIndex; TempIndex++) {\r
894 if (DescriptorBuffer[TempIndex] == CapsuleDataPtr64) {\r
895 Flag = TRUE;\r
896 break;\r
897 }\r
898 }\r
899 if (Flag) {\r
900 Index ++;\r
901 continue;\r
902 }\r
903 }\r
904 \r
905 //\r
906 // Cache BlockList which has been processed\r
907 //\r
908 DescriptorBuffer[ValidIndex++] = CapsuleDataPtr64;\r
909 Index ++;\r
910 }\r
911 }\r
912 \r
913 return EFI_SUCCESS;\r
914}\r
915\r
916/**\r
917 Capsule PPI service to coalesce a fragmented capsule in memory.\r
918\r
919 @param PeiServices General purpose services available to every PEIM.\r
920 @param MemoryBase Pointer to the base of a block of memory that we can walk\r
921 all over while trying to coalesce our buffers.\r
922 On output, this variable will hold the base address of\r
923 a coalesced capsule.\r
924 @param MemorySize Size of the memory region pointed to by MemoryBase.\r
925 On output, this variable will contain the size of the\r
926 coalesced capsule.\r
927\r
928 @retval EFI_NOT_FOUND if we can't determine the boot mode\r
929 if the boot mode is not flash-update\r
930 if we could not find the capsule descriptors\r
931\r
932 @retval EFI_BUFFER_TOO_SMALL\r
933 if we could not coalesce the capsule in the memory\r
934 region provided to us\r
935\r
936 @retval EFI_SUCCESS if there's no capsule, or if we processed the\r
937 capsule successfully.\r
938**/\r
939EFI_STATUS\r
940EFIAPI\r
941CapsuleCoalesce (\r
942 IN EFI_PEI_SERVICES **PeiServices,\r
943 IN OUT VOID **MemoryBase,\r
944 IN OUT UINTN *MemorySize\r
945 )\r
946{\r
947 UINTN Index;\r
948 UINTN Size;\r
949 UINTN VariableCount;\r
950 CHAR16 CapsuleVarName[30];\r
951 CHAR16 *TempVarName;\r
952 EFI_PHYSICAL_ADDRESS CapsuleDataPtr64; \r
953 EFI_STATUS Status;\r
954 EFI_BOOT_MODE BootMode;\r
955 EFI_PEI_READ_ONLY_VARIABLE2_PPI *PPIVariableServices;\r
956 EFI_PHYSICAL_ADDRESS *VariableArrayAddress;\r
957 MEMORY_RESOURCE_DESCRIPTOR *MemoryResource;\r
958#ifdef MDE_CPU_IA32\r
959 UINT16 CoalesceImageMachineType;\r
960 EFI_PHYSICAL_ADDRESS CoalesceImageEntryPoint;\r
961 COALESCE_ENTRY CoalesceEntry;\r
962 EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer;\r
963#endif\r
964\r
965 Index = 0;\r
966 VariableCount = 0;\r
967 CapsuleVarName[0] = 0;\r
968 CapsuleDataPtr64 = 0;\r
969\r
970 //\r
971 // Someone should have already ascertained the boot mode. If it's not\r
972 // capsule update, then return normally.\r
973 //\r
974 Status = PeiServicesGetBootMode (&BootMode);\r
975 if (EFI_ERROR (Status) || (BootMode != BOOT_ON_FLASH_UPDATE)) {\r
976 DEBUG ((EFI_D_ERROR, "Boot mode is not correct for capsule update path.\n")); \r
977 Status = EFI_NOT_FOUND;\r
978 goto Done;\r
979 }\r
980 \r
981 //\r
982 // User may set the same ScatterGatherList with several different variables,\r
983 // so cache all ScatterGatherList for check later.\r
984 //\r
985 Status = PeiServicesLocatePpi (\r
986 &gEfiPeiReadOnlyVariable2PpiGuid,\r
987 0,\r
988 NULL,\r
989 (VOID **) &PPIVariableServices\r
990 );\r
991 if (EFI_ERROR (Status)) {\r
992 goto Done;\r
993 }\r
994 Size = sizeof (CapsuleDataPtr64);\r
995 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME);\r
996 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);\r
997 while (TRUE) {\r
998 if (Index > 0) {\r
999 UnicodeValueToStringS (\r
1000 TempVarName,\r
1001 sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),\r
1002 0,\r
1003 Index,\r
1004 0\r
1005 );\r
1006 }\r
1007 Status = PPIVariableServices->GetVariable (\r
1008 PPIVariableServices,\r
1009 CapsuleVarName,\r
1010 &gEfiCapsuleVendorGuid,\r
1011 NULL,\r
1012 &Size,\r
1013 (VOID *) &CapsuleDataPtr64\r
1014 );\r
1015 if (EFI_ERROR (Status)) {\r
1016 //\r
1017 // There is no capsule variables, quit\r
1018 //\r
1019 DEBUG ((EFI_D_INFO,"Capsule variable Index = %d\n", Index));\r
1020 break;\r
1021 }\r
1022 VariableCount++;\r
1023 Index++;\r
1024 }\r
1025 \r
1026 DEBUG ((EFI_D_INFO,"Capsule variable count = %d\n", VariableCount));\r
1027 \r
1028 //\r
1029 // The last entry is the end flag.\r
1030 //\r
1031 Status = PeiServicesAllocatePool (\r
1032 (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS),\r
1033 (VOID **)&VariableArrayAddress\r
1034 );\r
1035\r
1036 if (Status != EFI_SUCCESS) {\r
1037 DEBUG ((EFI_D_ERROR, "AllocatePages Failed!, Status = %x\n", Status));\r
1038 goto Done;\r
1039 }\r
1040 \r
1041 ZeroMem (VariableArrayAddress, (VariableCount + 1) * sizeof (EFI_PHYSICAL_ADDRESS));\r
1042 \r
1043 //\r
1044 // Find out if we actually have a capsule.\r
1045 // GetCapsuleDescriptors depends on variable PPI, so it should run in 32-bit environment.\r
1046 //\r
1047 Status = GetCapsuleDescriptors (VariableArrayAddress);\r
1048 if (EFI_ERROR (Status)) {\r
1049 DEBUG ((EFI_D_ERROR, "Fail to find capsule variables.\n"));\r
1050 goto Done;\r
1051 }\r
1052\r
1053 MemoryResource = BuildMemoryResourceDescriptor ();\r
1054\r
1055#ifdef MDE_CPU_IA32\r
1056 if (FeaturePcdGet (PcdDxeIplSwitchToLongMode)) {\r
1057 //\r
1058 // Switch to 64-bit mode to process capsule data when:\r
1059 // 1. When DXE phase is 64-bit\r
1060 // 2. When the buffer for 64-bit transition exists\r
1061 // 3. When Capsule X64 image is built in BIOS image\r
1062 // In 64-bit mode, we can process capsule data above 4GB.\r
1063 //\r
1064 CoalesceImageEntryPoint = 0;\r
1065 Status = GetLongModeContext (&LongModeBuffer);\r
1066 if (EFI_ERROR (Status)) {\r
1067 DEBUG ((EFI_D_ERROR, "Fail to find the variable for long mode context!\n"));\r
1068 Status = EFI_NOT_FOUND;\r
1069 goto Done;\r
1070 }\r
1071 \r
1072 Status = FindCapsuleCoalesceImage (&CoalesceImageEntryPoint, &CoalesceImageMachineType);\r
1073 if ((EFI_ERROR (Status)) || (CoalesceImageMachineType != EFI_IMAGE_MACHINE_X64)) {\r
1074 DEBUG ((EFI_D_ERROR, "Fail to find CapsuleX64 module in FV!\n"));\r
1075 Status = EFI_NOT_FOUND;\r
1076 goto Done;\r
1077 }\r
1078 ASSERT (CoalesceImageEntryPoint != 0);\r
1079 CoalesceEntry = (COALESCE_ENTRY) (UINTN) CoalesceImageEntryPoint;\r
1080 Status = ModeSwitch (&LongModeBuffer, CoalesceEntry, (EFI_PHYSICAL_ADDRESS)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);\r
1081 } else {\r
1082 //\r
1083 // Capsule is processed in IA32 mode.\r
1084 //\r
1085 Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);\r
1086 }\r
1087#else\r
1088 //\r
1089 // Process capsule directly.\r
1090 //\r
1091 Status = CapsuleDataCoalesce (PeiServices, (EFI_PHYSICAL_ADDRESS *)(UINTN)VariableArrayAddress, MemoryResource, MemoryBase, MemorySize);\r
1092#endif\r
1093 \r
1094 DEBUG ((EFI_D_INFO, "Capsule Coalesce Status = %r!\n", Status));\r
1095\r
1096 if (Status == EFI_BUFFER_TOO_SMALL) {\r
1097 DEBUG ((EFI_D_ERROR, "There is not enough memory to process capsule!\n"));\r
1098 }\r
1099 \r
1100 if (Status == EFI_NOT_FOUND) {\r
1101 DEBUG ((EFI_D_ERROR, "Fail to parse capsule descriptor in memory!\n"));\r
1102 REPORT_STATUS_CODE (\r
1103 EFI_ERROR_CODE | EFI_ERROR_MAJOR,\r
1104 (EFI_SOFTWARE_PEI_MODULE | EFI_SW_PEI_EC_INVALID_CAPSULE_DESCRIPTOR)\r
1105 );\r
1106 }\r
1107\r
1108Done:\r
1109 return Status;\r
1110}\r
1111\r
1112/**\r
1113 Determine if we're in capsule update boot mode.\r
1114\r
1115 @param PeiServices PEI services table\r
1116\r
1117 @retval EFI_SUCCESS if we have a capsule available\r
1118 @retval EFI_NOT_FOUND no capsule detected\r
1119\r
1120**/\r
1121EFI_STATUS\r
1122EFIAPI\r
1123CheckCapsuleUpdate (\r
1124 IN EFI_PEI_SERVICES **PeiServices\r
1125 )\r
1126{\r
1127 EFI_STATUS Status;\r
1128 Status = GetCapsuleDescriptors (NULL);\r
1129 return Status;\r
1130}\r
1131/**\r
1132 This function will look at a capsule and determine if it's a test pattern. \r
1133 If it is, then it will verify it and emit an error message if corruption is detected.\r
1134 \r
1135 @param PeiServices Standard pei services pointer\r
1136 @param CapsuleBase Base address of coalesced capsule, which is preceeded\r
1137 by private data. Very implementation specific.\r
1138\r
1139 @retval TRUE Capsule image is the test image\r
1140 @retval FALSE Capsule image is not the test image.\r
1141\r
1142**/\r
1143BOOLEAN\r
1144CapsuleTestPattern (\r
1145 IN EFI_PEI_SERVICES **PeiServices,\r
1146 IN VOID *CapsuleBase\r
1147 )\r
1148{\r
1149 UINT32 *TestPtr;\r
1150 UINT32 TestCounter;\r
1151 UINT32 TestSize;\r
1152 BOOLEAN RetValue;\r
1153\r
1154 RetValue = FALSE;\r
1155\r
1156 //\r
1157 // Look at the capsule data and determine if it's a test pattern. If it\r
1158 // is, then test it now.\r
1159 //\r
1160 TestPtr = (UINT32 *) CapsuleBase;\r
1161 //\r
1162 // 0x54534554 "TEST"\r
1163 //\r
1164 if (*TestPtr == 0x54534554) {\r
1165 RetValue = TRUE;\r
1166 DEBUG ((EFI_D_INFO, "Capsule test pattern mode activated...\n"));\r
1167 TestSize = TestPtr[1] / sizeof (UINT32);\r
1168 //\r
1169 // Skip over the signature and the size fields in the pattern data header\r
1170 //\r
1171 TestPtr += 2;\r
1172 TestCounter = 0;\r
1173 while (TestSize > 0) {\r
1174 if (*TestPtr != TestCounter) {\r
1175 DEBUG ((EFI_D_INFO, "Capsule test pattern mode FAILED: BaseAddr/FailAddr 0x%X 0x%X\n", (UINT32)(UINTN)(EFI_CAPSULE_PEIM_PRIVATE_DATA *)CapsuleBase, (UINT32)(UINTN)TestPtr));\r
1176 return TRUE;\r
1177 }\r
1178\r
1179 TestPtr++;\r
1180 TestCounter++;\r
1181 TestSize--;\r
1182 }\r
1183\r
1184 DEBUG ((EFI_D_INFO, "Capsule test pattern mode SUCCESS\n"));\r
1185 }\r
1186\r
1187 return RetValue;\r
1188}\r
1189\r
1190/**\r
1191 Capsule PPI service that gets called after memory is available. The\r
1192 capsule coalesce function, which must be called first, returns a base\r
1193 address and size, which can be anything actually. Once the memory init\r
1194 PEIM has discovered memory, then it should call this function and pass in\r
1195 the base address and size returned by the coalesce function. Then this\r
1196 function can create a capsule HOB and return.\r
1197\r
1198 @param PeiServices standard pei services pointer\r
1199 @param CapsuleBase address returned by the capsule coalesce function. Most\r
1200 likely this will actually be a pointer to private data.\r
1201 @param CapsuleSize value returned by the capsule coalesce function.\r
1202\r
1203 @retval EFI_VOLUME_CORRUPTED CapsuleBase does not appear to point to a\r
1204 coalesced capsule\r
1205 @retval EFI_SUCCESS if all goes well.\r
1206**/\r
1207EFI_STATUS\r
1208EFIAPI\r
1209CreateState (\r
1210 IN EFI_PEI_SERVICES **PeiServices,\r
1211 IN VOID *CapsuleBase,\r
1212 IN UINTN CapsuleSize\r
1213 )\r
1214{\r
1215 EFI_STATUS Status;\r
1216 EFI_CAPSULE_PEIM_PRIVATE_DATA *PrivateData;\r
1217 UINTN Size;\r
1218 EFI_PHYSICAL_ADDRESS NewBuffer;\r
1219 UINTN CapsuleNumber;\r
1220 UINT32 Index;\r
1221 EFI_PHYSICAL_ADDRESS BaseAddress;\r
1222 UINT64 Length;\r
1223 \r
1224 PrivateData = (EFI_CAPSULE_PEIM_PRIVATE_DATA *) CapsuleBase;\r
1225 if (PrivateData->Signature != EFI_CAPSULE_PEIM_PRIVATE_DATA_SIGNATURE) {\r
1226 return EFI_VOLUME_CORRUPTED;\r
1227 }\r
1228 if (PrivateData->CapsuleAllImageSize >= MAX_ADDRESS) {\r
1229 DEBUG ((EFI_D_ERROR, "CapsuleAllImageSize too big - 0x%lx\n", PrivateData->CapsuleAllImageSize));\r
1230 return EFI_OUT_OF_RESOURCES;\r
1231 }\r
1232 if (PrivateData->CapsuleNumber >= MAX_ADDRESS) {\r
1233 DEBUG ((EFI_D_ERROR, "CapsuleNumber too big - 0x%lx\n", PrivateData->CapsuleNumber));\r
1234 return EFI_OUT_OF_RESOURCES;\r
1235 }\r
1236 //\r
1237 // Capsule Number and Capsule Offset is in the tail of Capsule data.\r
1238 //\r
1239 Size = (UINTN)PrivateData->CapsuleAllImageSize;\r
1240 CapsuleNumber = (UINTN)PrivateData->CapsuleNumber;\r
1241 //\r
1242 // Allocate the memory so that it gets preserved into DXE\r
1243 //\r
1244 Status = PeiServicesAllocatePages (\r
1245 EfiRuntimeServicesData,\r
1246 EFI_SIZE_TO_PAGES (Size),\r
1247 &NewBuffer\r
1248 );\r
1249\r
1250 if (Status != EFI_SUCCESS) {\r
1251 DEBUG ((EFI_D_ERROR, "AllocatePages Failed!\n"));\r
1252 return Status;\r
1253 }\r
1254 //\r
1255 // Copy to our new buffer for DXE\r
1256 //\r
1257 DEBUG ((EFI_D_INFO, "Capsule copy from 0x%8X to 0x%8X with size 0x%8X\n", (UINTN)((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), (UINTN) NewBuffer, Size));\r
1258 CopyMem ((VOID *) (UINTN) NewBuffer, (VOID *) (UINTN) ((UINT8 *)PrivateData + sizeof(EFI_CAPSULE_PEIM_PRIVATE_DATA) + (CapsuleNumber - 1) * sizeof(UINT64)), Size);\r
1259 //\r
1260 // Check for test data pattern. If it is the test pattern, then we'll\r
1261 // test it and still create the HOB so that it can be used to verify\r
1262 // that capsules don't get corrupted all the way into BDS. BDS will\r
1263 // still try to turn it into a firmware volume, but will think it's\r
1264 // corrupted so nothing will happen.\r
1265 //\r
1266 DEBUG_CODE (\r
1267 CapsuleTestPattern (PeiServices, (VOID *) (UINTN) NewBuffer);\r
1268 );\r
1269\r
1270 //\r
1271 // Build the UEFI Capsule Hob for each capsule image.\r
1272 //\r
1273 for (Index = 0; Index < CapsuleNumber; Index ++) {\r
1274 BaseAddress = NewBuffer + PrivateData->CapsuleOffset[Index];\r
1275 Length = ((EFI_CAPSULE_HEADER *)((UINTN) BaseAddress))->CapsuleImageSize;\r
1276\r
1277 BuildCvHob (BaseAddress, Length);\r
1278 }\r
1279 \r
1280 return EFI_SUCCESS;\r
1281}\r
1282\r
1283CONST EFI_PEI_CAPSULE_PPI mCapsulePpi = {\r
1284 CapsuleCoalesce,\r
1285 CheckCapsuleUpdate,\r
1286 CreateState\r
1287};\r
1288\r
1289CONST EFI_PEI_PPI_DESCRIPTOR mUefiPpiListCapsule = {\r
1290 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
1291 &gEfiPeiCapsulePpiGuid,\r
1292 (EFI_PEI_CAPSULE_PPI *) &mCapsulePpi\r
1293};\r
1294\r
1295/**\r
1296 Entry point function for the PEIM\r
1297\r
1298 @param FileHandle Handle of the file being invoked.\r
1299 @param PeiServices Describes the list of possible PEI Services.\r
1300\r
1301 @return EFI_SUCCESS If we installed our PPI\r
1302\r
1303**/\r
1304EFI_STATUS\r
1305EFIAPI\r
1306CapsuleMain (\r
1307 IN EFI_PEI_FILE_HANDLE FileHandle,\r
1308 IN CONST EFI_PEI_SERVICES **PeiServices\r
1309 )\r
1310{\r
1311 //\r
1312 // Just produce our PPI\r
1313 //\r
1314 return PeiServicesInstallPpi (&mUefiPpiListCapsule);\r
1315}\r