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
IsPage1GSupport(VOID)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
CalculatePageTableSize(IN BOOLEAN Page1GSupport)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
Create4GPageTables(IN EFI_PHYSICAL_ADDRESS PageTablesAddress,IN BOOLEAN Page1GSupport)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
ReturnFunction(SWITCH_32_TO_64_CONTEXT * EntrypointContext,SWITCH_64_TO_32_CONTEXT * ReturnContext)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
Thunk32To64(EFI_PHYSICAL_ADDRESS PageTableAddress,SWITCH_32_TO_64_CONTEXT * Context,SWITCH_64_TO_32_CONTEXT * ReturnContext)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
ModeSwitch(IN EFI_CAPSULE_LONG_MODE_BUFFER * LongModeBuffer,IN COALESCE_ENTRY CoalesceEntry,IN EFI_PHYSICAL_ADDRESS BlockListAddr,IN OUT VOID ** MemoryBase,IN OUT UINTN * MemorySize)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
FindCapsuleCoalesceImage(OUT EFI_PHYSICAL_ADDRESS * CoalesceImageEntryPoint,OUT UINT16 * CoalesceImageMachineType)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
GetLongModeContext(OUT EFI_CAPSULE_LONG_MODE_BUFFER * LongModeBuffer)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
GetCapsuleDescriptors(IN EFI_PHYSICAL_ADDRESS * DescriptorBuffer)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
CapsuleCoalesce(IN EFI_PEI_SERVICES ** PeiServices,IN OUT VOID ** MemoryBase,IN OUT UINTN * MemorySize)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
CheckCapsuleUpdate(IN EFI_PEI_SERVICES ** PeiServices)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
CapsuleTestPattern(IN EFI_PEI_SERVICES ** PeiServices,IN VOID * CapsuleBase)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
CreateState(IN EFI_PEI_SERVICES ** PeiServices,IN VOID * CapsuleBase,IN UINTN CapsuleSize)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
CapsuleMain(IN EFI_PEI_FILE_HANDLE FileHandle,IN CONST EFI_PEI_SERVICES ** PeiServices)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 }
1064