• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2   DXE capsule process.
3 
4   Caution: This module requires additional review when modified.
5   This module will have external input - capsule image.
6   This external input must be validated carefully to avoid security issue like
7   buffer overflow, integer overflow.
8 
9   ProcessCapsules(), ProcessTheseCapsules() will receive untrusted
10   input and do basic validation.
11 
12   Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
13   This program and the accompanying materials
14   are licensed and made available under the terms and conditions of the BSD License
15   which accompanies this distribution.  The full text of the license may be found at
16   http://opensource.org/licenses/bsd-license.php
17 
18   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
19   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 
21 **/
22 
23 #include <PiDxe.h>
24 #include <Protocol/EsrtManagement.h>
25 
26 #include <Library/BaseLib.h>
27 #include <Library/DebugLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/UefiBootServicesTableLib.h>
30 #include <Library/UefiRuntimeServicesTableLib.h>
31 #include <Library/MemoryAllocationLib.h>
32 #include <Library/UefiLib.h>
33 #include <Library/PcdLib.h>
34 #include <Library/HobLib.h>
35 #include <Library/ReportStatusCodeLib.h>
36 #include <Library/CapsuleLib.h>
37 
38 #include <IndustryStandard/WindowsUxCapsule.h>
39 
40 /**
41   Return if this FMP is a system FMP or a device FMP, based upon CapsuleHeader.
42 
43   @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER
44 
45   @retval TRUE  It is a system FMP.
46   @retval FALSE It is a device FMP.
47 **/
48 BOOLEAN
49 IsFmpCapsule (
50   IN EFI_CAPSULE_HEADER  *CapsuleHeader
51   );
52 
53 /**
54   Validate Fmp capsules layout.
55 
56   Caution: This function may receive untrusted input.
57 
58   This function assumes the caller validated the capsule by using
59   IsValidCapsuleHeader(), so that all fields in EFI_CAPSULE_HEADER are correct.
60   The capsule buffer size is CapsuleHeader->CapsuleImageSize.
61 
62   This function validates the fields in EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
63   and EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.
64 
65   This function need support nested FMP capsule.
66 
67   @param[in]   CapsuleHeader        Points to a capsule header.
68   @param[out]  EmbeddedDriverCount  The EmbeddedDriverCount in the FMP capsule.
69 
70   @retval EFI_SUCESS             Input capsule is a correct FMP capsule.
71   @retval EFI_INVALID_PARAMETER  Input capsule is not a correct FMP capsule.
72 **/
73 EFI_STATUS
74 ValidateFmpCapsule (
75   IN EFI_CAPSULE_HEADER  *CapsuleHeader,
76   OUT UINT16             *EmbeddedDriverCount OPTIONAL
77   );
78 
79 /**
80   Validate if it is valid capsule header
81 
82   This function assumes the caller provided correct CapsuleHeader pointer
83   and CapsuleSize.
84 
85   This function validates the fields in EFI_CAPSULE_HEADER.
86 
87   @param[in]  CapsuleHeader    Points to a capsule header.
88   @param[in]  CapsuleSize      Size of the whole capsule image.
89 
90 **/
91 BOOLEAN
92 IsValidCapsuleHeader (
93   IN EFI_CAPSULE_HEADER  *CapsuleHeader,
94   IN UINT64              CapsuleSize
95   );
96 
97 extern BOOLEAN                   mDxeCapsuleLibEndOfDxe;
98 BOOLEAN                          mNeedReset;
99 
100 VOID                        **mCapsulePtr;
101 EFI_STATUS                  *mCapsuleStatusArray;
102 UINT32                      mCapsuleTotalNumber;
103 
104 /**
105   This function initializes the mCapsulePtr, mCapsuleStatusArray and mCapsuleTotalNumber.
106 **/
107 VOID
InitCapsulePtr(VOID)108 InitCapsulePtr (
109   VOID
110   )
111 {
112   EFI_PEI_HOB_POINTERS        HobPointer;
113   UINTN                       Index;
114 
115   //
116   // Find all capsule images from hob
117   //
118   HobPointer.Raw = GetHobList ();
119   while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) {
120     if (!IsValidCapsuleHeader((VOID *)(UINTN)HobPointer.Capsule->BaseAddress, HobPointer.Capsule->Length)) {
121       HobPointer.Header->HobType = EFI_HOB_TYPE_UNUSED; // Mark this hob as invalid
122     } else {
123       mCapsuleTotalNumber++;
124     }
125     HobPointer.Raw = GET_NEXT_HOB (HobPointer);
126   }
127 
128   DEBUG ((DEBUG_INFO, "mCapsuleTotalNumber - 0x%x\n", mCapsuleTotalNumber));
129 
130   if (mCapsuleTotalNumber == 0) {
131     return ;
132   }
133 
134   //
135   // Init temp Capsule Data table.
136   //
137   mCapsulePtr       = (VOID **) AllocateZeroPool (sizeof (VOID *) * mCapsuleTotalNumber);
138   if (mCapsulePtr == NULL) {
139     DEBUG ((DEBUG_ERROR, "Allocate mCapsulePtr fail!\n"));
140     mCapsuleTotalNumber = 0;
141     return ;
142   }
143   mCapsuleStatusArray = (EFI_STATUS *) AllocateZeroPool (sizeof (EFI_STATUS) * mCapsuleTotalNumber);
144   if (mCapsuleStatusArray == NULL) {
145     DEBUG ((DEBUG_ERROR, "Allocate mCapsuleStatusArray fail!\n"));
146     FreePool (mCapsulePtr);
147     mCapsulePtr = NULL;
148     mCapsuleTotalNumber = 0;
149     return ;
150   }
151   SetMemN (mCapsuleStatusArray, sizeof (EFI_STATUS) * mCapsuleTotalNumber, EFI_NOT_READY);
152 
153   //
154   // Find all capsule images from hob
155   //
156   HobPointer.Raw = GetHobList ();
157   Index = 0;
158   while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) {
159     mCapsulePtr [Index++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress;
160     HobPointer.Raw = GET_NEXT_HOB (HobPointer);
161   }
162 }
163 
164 /**
165   This function returns if all capsule images are processed.
166 
167   @retval TRUE   All capsule images are processed.
168   @retval FALSE  Not all capsule images are processed.
169 **/
170 BOOLEAN
AreAllImagesProcessed(VOID)171 AreAllImagesProcessed (
172   VOID
173   )
174 {
175   UINTN  Index;
176 
177   for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
178     if (mCapsuleStatusArray[Index] == EFI_NOT_READY) {
179       return FALSE;
180     }
181   }
182 
183   return TRUE;
184 }
185 
186 /**
187   This function populates capsule in the configuration table.
188 **/
189 VOID
PopulateCapsuleInConfigurationTable(VOID)190 PopulateCapsuleInConfigurationTable (
191   VOID
192   )
193 {
194   VOID                        **CapsulePtrCache;
195   EFI_GUID                    *CapsuleGuidCache;
196   EFI_CAPSULE_HEADER          *CapsuleHeader;
197   EFI_CAPSULE_TABLE           *CapsuleTable;
198   UINT32                      CacheIndex;
199   UINT32                      CacheNumber;
200   UINT32                      CapsuleNumber;
201   UINTN                       Index;
202   UINTN                       Size;
203   EFI_STATUS                  Status;
204 
205   if (mCapsuleTotalNumber == 0) {
206     return ;
207   }
208 
209   CapsulePtrCache     = NULL;
210   CapsuleGuidCache    = NULL;
211   CacheIndex          = 0;
212   CacheNumber         = 0;
213 
214   CapsulePtrCache  = (VOID **) AllocateZeroPool (sizeof (VOID *) * mCapsuleTotalNumber);
215   if (CapsulePtrCache == NULL) {
216     DEBUG ((DEBUG_ERROR, "Allocate CapsulePtrCache fail!\n"));
217     return ;
218   }
219   CapsuleGuidCache = (EFI_GUID *) AllocateZeroPool (sizeof (EFI_GUID) * mCapsuleTotalNumber);
220   if (CapsuleGuidCache == NULL) {
221     DEBUG ((DEBUG_ERROR, "Allocate CapsuleGuidCache fail!\n"));
222     FreePool (CapsulePtrCache);
223     return ;
224   }
225 
226   //
227   // Capsules who have CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE always are used for operating
228   // System to have information persist across a system reset. EFI System Table must
229   // point to an array of capsules that contains the same CapsuleGuid value. And agents
230   // searching for this type capsule will look in EFI System Table and search for the
231   // capsule's Guid and associated pointer to retrieve the data. Two steps below describes
232   // how to sorting the capsules by the unique guid and install the array to EFI System Table.
233   // Firstly, Loop for all coalesced capsules, record unique CapsuleGuids and cache them in an
234   // array for later sorting capsules by CapsuleGuid.
235   //
236   for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
237     CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
238     if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) {
239       //
240       // For each capsule, we compare it with known CapsuleGuid in the CacheArray.
241       // If already has the Guid, skip it. Whereas, record it in the CacheArray as
242       // an additional one.
243       //
244       CacheIndex = 0;
245       while (CacheIndex < CacheNumber) {
246         if (CompareGuid(&CapsuleGuidCache[CacheIndex],&CapsuleHeader->CapsuleGuid)) {
247           break;
248         }
249         CacheIndex++;
250       }
251       if (CacheIndex == CacheNumber) {
252         CopyMem(&CapsuleGuidCache[CacheNumber++],&CapsuleHeader->CapsuleGuid,sizeof(EFI_GUID));
253       }
254     }
255   }
256 
257   //
258   // Secondly, for each unique CapsuleGuid in CacheArray, gather all coalesced capsules
259   // whose guid is the same as it, and malloc memory for an array which preceding
260   // with UINT32. The array fills with entry point of capsules that have the same
261   // CapsuleGuid, and UINT32 represents the size of the array of capsules. Then install
262   // this array into EFI System Table, so that agents searching for this type capsule
263   // will look in EFI System Table and search for the capsule's Guid and associated
264   // pointer to retrieve the data.
265   //
266   for (CacheIndex = 0; CacheIndex < CacheNumber; CacheIndex++) {
267     CapsuleNumber = 0;
268     for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
269       CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
270       if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) {
271         if (CompareGuid (&CapsuleGuidCache[CacheIndex], &CapsuleHeader->CapsuleGuid)) {
272           //
273           // Cache Caspuleheader to the array, this array is uniqued with certain CapsuleGuid.
274           //
275           CapsulePtrCache[CapsuleNumber++] = (VOID*)CapsuleHeader;
276         }
277       }
278     }
279     if (CapsuleNumber != 0) {
280       Size = sizeof(EFI_CAPSULE_TABLE) + (CapsuleNumber - 1) * sizeof(VOID*);
281       CapsuleTable = AllocateRuntimePool (Size);
282       if (CapsuleTable == NULL) {
283         DEBUG ((DEBUG_ERROR, "Allocate CapsuleTable (%g) fail!\n", &CapsuleGuidCache[CacheIndex]));
284         continue;
285       }
286       CapsuleTable->CapsuleArrayNumber =  CapsuleNumber;
287       CopyMem(&CapsuleTable->CapsulePtr[0], CapsulePtrCache, CapsuleNumber * sizeof(VOID*));
288       Status = gBS->InstallConfigurationTable (&CapsuleGuidCache[CacheIndex], (VOID*)CapsuleTable);
289       if (EFI_ERROR (Status)) {
290         DEBUG ((DEBUG_ERROR, "InstallConfigurationTable (%g) fail!\n", &CapsuleGuidCache[CacheIndex]));
291       }
292     }
293   }
294 
295   FreePool(CapsuleGuidCache);
296   FreePool(CapsulePtrCache);
297 }
298 
299 /**
300 
301   This routine is called to process capsules.
302 
303   Caution: This function may receive untrusted input.
304 
305   Each individual capsule result is recorded in capsule record variable.
306 
307   @param[in]  FirstRound         TRUE:  First round. Need skip the FMP capsules with non zero EmbeddedDriverCount.
308                                  FALSE: Process rest FMP capsules.
309 
310   @retval EFI_SUCCESS             There is no error when processing capsules.
311   @retval EFI_OUT_OF_RESOURCES    No enough resource to process capsules.
312 
313 **/
314 EFI_STATUS
ProcessTheseCapsules(IN BOOLEAN FirstRound)315 ProcessTheseCapsules (
316   IN BOOLEAN  FirstRound
317   )
318 {
319   EFI_STATUS                  Status;
320   EFI_CAPSULE_HEADER          *CapsuleHeader;
321   UINT32                      Index;
322   BOOLEAN                     DisplayCapsuleExist;
323   ESRT_MANAGEMENT_PROTOCOL    *EsrtManagement;
324   UINT16                      EmbeddedDriverCount;
325 
326   REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesBegin)));
327 
328   if (FirstRound) {
329     InitCapsulePtr ();
330   }
331 
332   if (mCapsuleTotalNumber == 0) {
333     //
334     // We didn't find a hob, so had no errors.
335     //
336     DEBUG ((DEBUG_ERROR, "We can not find capsule data in capsule update boot mode.\n"));
337     return EFI_SUCCESS;
338   }
339 
340   if (AreAllImagesProcessed ()) {
341     return EFI_SUCCESS;
342   }
343 
344   //
345   // Check the capsule flags,if contains CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE, install
346   // capsuleTable to configure table with EFI_CAPSULE_GUID
347   //
348   if (FirstRound) {
349     PopulateCapsuleInConfigurationTable ();
350   }
351 
352   REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdatingFirmware)));
353 
354   //
355   // If Windows UX capsule exist, process it first
356   //
357   DisplayCapsuleExist = FALSE;
358   for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
359     CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
360     if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {
361       DEBUG ((DEBUG_INFO, "ProcessCapsuleImage (Ux) - 0x%x\n", CapsuleHeader));
362       DisplayCapsuleExist = TRUE;
363       DEBUG ((DEBUG_INFO, "Display logo capsule is found.\n"));
364       Status = ProcessCapsuleImage (CapsuleHeader);
365       mCapsuleStatusArray [Index] = EFI_SUCCESS;
366       DEBUG((DEBUG_INFO, "ProcessCapsuleImage (Ux) - %r\n", Status));
367       break;
368     }
369   }
370 
371   if (!DisplayCapsuleExist) {
372     //
373     // Display Capsule not found. Display the default string.
374     //
375     Print (L"Updating the firmware ......\r\n");
376   }
377 
378   //
379   // All capsules left are recognized by platform.
380   //
381   for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
382     if (mCapsuleStatusArray [Index] != EFI_NOT_READY) {
383       // already processed
384       continue;
385     }
386     CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
387     if (!CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {
388       //
389       // Call capsule library to process capsule image.
390       //
391       EmbeddedDriverCount = 0;
392       if (IsFmpCapsule(CapsuleHeader)) {
393         Status = ValidateFmpCapsule (CapsuleHeader, &EmbeddedDriverCount);
394         if (EFI_ERROR(Status)) {
395           DEBUG((DEBUG_ERROR, "ValidateFmpCapsule failed. Ignore!\n"));
396           mCapsuleStatusArray [Index] = EFI_ABORTED;
397           continue;
398         }
399       } else {
400         mCapsuleStatusArray [Index] = EFI_ABORTED;
401         continue;
402       }
403 
404       if ((!FirstRound) || (EmbeddedDriverCount == 0)) {
405         DEBUG((DEBUG_INFO, "ProcessCapsuleImage - 0x%x\n", CapsuleHeader));
406         Status = ProcessCapsuleImage (CapsuleHeader);
407         mCapsuleStatusArray [Index] = Status;
408         DEBUG((DEBUG_INFO, "ProcessCapsuleImage - %r\n", Status));
409 
410         if (Status != EFI_NOT_READY) {
411           if (EFI_ERROR(Status)) {
412             REPORT_STATUS_CODE(EFI_ERROR_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdateFirmwareFailed)));
413             DEBUG ((DEBUG_ERROR, "Capsule process failed!\n"));
414             Print (L"Firmware update failed...\r\n");
415           } else {
416             REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdateFirmwareSuccess)));
417           }
418 
419           if ((CapsuleHeader->Flags & PcdGet16(PcdSystemRebootAfterCapsuleProcessFlag)) != 0 ||
420               IsFmpCapsule(CapsuleHeader)) {
421             mNeedReset = TRUE;
422           }
423         }
424       }
425     }
426   }
427 
428   Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtManagement);
429   //
430   // Always sync ESRT Cache from FMP Instance
431   //
432   if (!EFI_ERROR(Status)) {
433     EsrtManagement->SyncEsrtFmp();
434   }
435   Status = EFI_SUCCESS;
436 
437   REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesEnd)));
438 
439   return Status;
440 }
441 
442 /**
443   Do reset system.
444 **/
445 VOID
DoResetSystem(VOID)446 DoResetSystem (
447   VOID
448   )
449 {
450   UINTN                         Index;
451 
452   REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeResettingSystem)));
453 
454   Print(L"Capsule Request Cold Reboot.\n");
455   DEBUG((DEBUG_INFO, "Capsule Request Cold Reboot."));
456 
457   for (Index = 5; Index > 0; Index--) {
458     Print(L"\rResetting system in %d seconds ...", Index);
459     DEBUG((DEBUG_INFO, "\rResetting system in %d seconds ...", Index));
460     gBS->Stall(1000000);
461   }
462 
463   gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
464 
465   CpuDeadLoop();
466 }
467 
468 /**
469 
470   This routine is called to process capsules.
471 
472   Caution: This function may receive untrusted input.
473 
474   The capsules reported in EFI_HOB_UEFI_CAPSULE are processed.
475   If there is no EFI_HOB_UEFI_CAPSULE, this routine does nothing.
476 
477   This routine should be called twice in BDS.
478   1) The first call must be before EndOfDxe. The system capsules is processed.
479      If device capsule FMP protocols are exposted at this time and device FMP
480      capsule has zero EmbeddedDriverCount, the device capsules are processed.
481      Each individual capsule result is recorded in capsule record variable.
482      System may reset in this function, if reset is required by capsule and
483      all capsules are processed.
484      If not all capsules are processed, reset will be defered to second call.
485 
486   2) The second call must be after EndOfDxe and after ConnectAll, so that all
487      device capsule FMP protocols are exposed.
488      The system capsules are skipped. If the device capsules are NOT processed
489      in first call, they are processed here.
490      Each individual capsule result is recorded in capsule record variable.
491      System may reset in this function, if reset is required by capsule
492      processed in first call and second call.
493 
494   @retval EFI_SUCCESS             There is no error when processing capsules.
495   @retval EFI_OUT_OF_RESOURCES    No enough resource to process capsules.
496 
497 **/
498 EFI_STATUS
499 EFIAPI
ProcessCapsules(VOID)500 ProcessCapsules (
501   VOID
502   )
503 {
504   EFI_STATUS                    Status;
505 
506   if (!mDxeCapsuleLibEndOfDxe) {
507     Status = ProcessTheseCapsules(TRUE);
508 
509     //
510     // Reboot System if and only if all capsule processed.
511     // If not, defer reset to 2nd process.
512     //
513     if (mNeedReset && AreAllImagesProcessed()) {
514       DoResetSystem();
515     }
516   } else {
517     Status = ProcessTheseCapsules(FALSE);
518     //
519     // Reboot System if required after all capsule processed
520     //
521     if (mNeedReset) {
522       DoResetSystem();
523     }
524   }
525   return Status;
526 }
527