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