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