Home | History | Annotate | Download | only in DxeCapsuleLibFmp
      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
    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
    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
    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
    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
    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
    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