| /** @file | |
| BDS routines to handle capsules. | |
| Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include "Bds.h" | |
| /** | |
| This routine is called to see if there are any capsules we need to process. | |
| If the boot mode is not UPDATE, then we do nothing. Otherwise find the | |
| capsule HOBS and produce firmware volumes for them via the DXE service. | |
| Then call the dispatcher to dispatch drivers from them. Finally, check | |
| the status of the updates. | |
| This function should be called by BDS in case we need to do some | |
| sort of processing even if there is no capsule to process. We | |
| need to do this if an earlier update went away and we need to | |
| clear the capsule variable so on the next reset PEI does not see it and | |
| think there is a capsule available. | |
| @param BootMode the current boot mode | |
| @retval EFI_INVALID_PARAMETER boot mode is not correct for an update | |
| @retval EFI_SUCCESS There is no error when processing capsule | |
| **/ | |
| EFI_STATUS | |
| EFIAPI | |
| BdsProcessCapsules ( | |
| EFI_BOOT_MODE BootMode | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| EFI_PEI_HOB_POINTERS HobPointer; | |
| EFI_CAPSULE_HEADER *CapsuleHeader; | |
| UINT32 Size; | |
| UINT32 CapsuleNumber; | |
| UINT32 CapsuleTotalNumber; | |
| EFI_CAPSULE_TABLE *CapsuleTable; | |
| UINT32 Index; | |
| UINT32 CacheIndex; | |
| UINT32 CacheNumber; | |
| VOID **CapsulePtr; | |
| VOID **CapsulePtrCache; | |
| EFI_GUID *CapsuleGuidCache; | |
| BOOLEAN NeedReset; | |
| CapsuleNumber = 0; | |
| CapsuleTotalNumber = 0; | |
| CacheIndex = 0; | |
| CacheNumber = 0; | |
| CapsulePtr = NULL; | |
| CapsulePtrCache = NULL; | |
| CapsuleGuidCache = NULL; | |
| NeedReset = FALSE; | |
| // | |
| // We don't do anything else if the boot mode is not flash-update | |
| // | |
| if (BootMode != BOOT_ON_FLASH_UPDATE) { | |
| DEBUG ((EFI_D_ERROR, "Boot mode is not correct for capsule update.\n")); | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| Status = EFI_SUCCESS; | |
| // | |
| // Find all capsule images from hob | |
| // | |
| HobPointer.Raw = GetHobList (); | |
| while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) { | |
| CapsuleTotalNumber ++; | |
| HobPointer.Raw = GET_NEXT_HOB (HobPointer); | |
| } | |
| if (CapsuleTotalNumber == 0) { | |
| // | |
| // We didn't find a hob, so had no errors. | |
| // | |
| DEBUG ((EFI_D_ERROR, "We can not find capsule data in capsule update boot mode.\n")); | |
| DEBUG ((EFI_D_ERROR, "Please check the followings are correct if unexpected capsule update error happens.\n")); | |
| DEBUG ((EFI_D_ERROR, "1. CapsuleX64 is built as X64 module when PEI is IA32 and DXE is X64\n")); | |
| DEBUG ((EFI_D_ERROR, "2. Capsule data should persist in memory across a system reset.\n")); | |
| PlatformBdsLockNonUpdatableFlash (); | |
| return EFI_SUCCESS; | |
| } | |
| // | |
| // Init temp Capsule Data table. | |
| // | |
| CapsulePtr = (VOID **) AllocateZeroPool (sizeof (VOID *) * CapsuleTotalNumber); | |
| ASSERT (CapsulePtr != NULL); | |
| CapsulePtrCache = (VOID **) AllocateZeroPool (sizeof (VOID *) * CapsuleTotalNumber); | |
| ASSERT (CapsulePtrCache != NULL); | |
| CapsuleGuidCache = (EFI_GUID *) AllocateZeroPool (sizeof (EFI_GUID) * CapsuleTotalNumber); | |
| ASSERT (CapsuleGuidCache != NULL); | |
| // | |
| // Find all capsule images from hob | |
| // | |
| HobPointer.Raw = GetHobList (); | |
| while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) { | |
| CapsulePtr [CapsuleNumber++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress; | |
| HobPointer.Raw = GET_NEXT_HOB (HobPointer); | |
| } | |
| // | |
| //Check the capsule flags,if contains CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE, install | |
| //capsuleTable to configure table with EFI_CAPSULE_GUID | |
| // | |
| // | |
| // Capsules who have CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE always are used for operating | |
| // System to have information persist across a system reset. EFI System Table must | |
| // point to an array of capsules that contains the same CapsuleGuid value. And agents | |
| // searching for this type capsule will look in EFI System Table and search for the | |
| // capsule's Guid and associated pointer to retrieve the data. Two steps below describes | |
| // how to sorting the capsules by the unique guid and install the array to EFI System Table. | |
| // Firstly, Loop for all coalesced capsules, record unique CapsuleGuids and cache them in an | |
| // array for later sorting capsules by CapsuleGuid. | |
| // | |
| for (Index = 0; Index < CapsuleTotalNumber; Index++) { | |
| CapsuleHeader = (EFI_CAPSULE_HEADER*) CapsulePtr [Index]; | |
| if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) { | |
| // | |
| // For each capsule, we compare it with known CapsuleGuid in the CacheArray. | |
| // If already has the Guid, skip it. Whereas, record it in the CacheArray as | |
| // an additional one. | |
| // | |
| CacheIndex = 0; | |
| while (CacheIndex < CacheNumber) { | |
| if (CompareGuid(&CapsuleGuidCache[CacheIndex],&CapsuleHeader->CapsuleGuid)) { | |
| break; | |
| } | |
| CacheIndex++; | |
| } | |
| if (CacheIndex == CacheNumber) { | |
| CopyMem(&CapsuleGuidCache[CacheNumber++],&CapsuleHeader->CapsuleGuid,sizeof(EFI_GUID)); | |
| } | |
| } | |
| } | |
| // | |
| // Secondly, for each unique CapsuleGuid in CacheArray, gather all coalesced capsules | |
| // whose guid is the same as it, and malloc memory for an array which preceding | |
| // with UINT32. The array fills with entry point of capsules that have the same | |
| // CapsuleGuid, and UINT32 represents the size of the array of capsules. Then install | |
| // this array into EFI System Table, so that agents searching for this type capsule | |
| // will look in EFI System Table and search for the capsule's Guid and associated | |
| // pointer to retrieve the data. | |
| // | |
| CacheIndex = 0; | |
| while (CacheIndex < CacheNumber) { | |
| CapsuleNumber = 0; | |
| for (Index = 0; Index < CapsuleTotalNumber; Index++) { | |
| CapsuleHeader = (EFI_CAPSULE_HEADER*) CapsulePtr [Index]; | |
| if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) { | |
| if (CompareGuid (&CapsuleGuidCache[CacheIndex], &CapsuleHeader->CapsuleGuid)) { | |
| // | |
| // Cache Caspuleheader to the array, this array is uniqued with certain CapsuleGuid. | |
| // | |
| CapsulePtrCache[CapsuleNumber++] = (VOID*)CapsuleHeader; | |
| } | |
| } | |
| } | |
| if (CapsuleNumber != 0) { | |
| Size = sizeof(EFI_CAPSULE_TABLE) + (CapsuleNumber - 1) * sizeof(VOID*); | |
| CapsuleTable = AllocateRuntimePool (Size); | |
| ASSERT (CapsuleTable != NULL); | |
| CapsuleTable->CapsuleArrayNumber = CapsuleNumber; | |
| CopyMem(&CapsuleTable->CapsulePtr[0], CapsulePtrCache, CapsuleNumber * sizeof(VOID*)); | |
| Status = gBS->InstallConfigurationTable (&CapsuleGuidCache[CacheIndex], (VOID*)CapsuleTable); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| CacheIndex++; | |
| } | |
| // | |
| // Besides ones with CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE flag, all capsules left are | |
| // recognized by platform with CapsuleGuid. For general platform driver, UpdateFlash | |
| // type is commonly supported, so here only deal with encapsuled FVs capsule. Additional | |
| // type capsule transaction could be extended. It depends on platform policy. | |
| // | |
| for (Index = 0; Index < CapsuleTotalNumber; Index++) { | |
| CapsuleHeader = (EFI_CAPSULE_HEADER*) CapsulePtr [Index]; | |
| if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) == 0) { | |
| // | |
| // Always reset system after all capsule processed if FMP capsule exist | |
| // | |
| if (CompareGuid (&gEfiFmpCapsuleGuid, &CapsuleHeader->CapsuleGuid)){ | |
| NeedReset = TRUE; | |
| } | |
| // | |
| // Call capsule library to process capsule image. | |
| // | |
| ProcessCapsuleImage (CapsuleHeader); | |
| } | |
| } | |
| if (NeedReset) { | |
| Print(L"Capsule Request Cold Reboot.\n"); | |
| for (Index = 5; Index > 0; Index--) { | |
| Print(L"\rResetting system in %d seconds ...", Index); | |
| gBS->Stall (1000000); | |
| } | |
| gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); | |
| CpuDeadLoop (); | |
| } | |
| PlatformBdsLockNonUpdatableFlash (); | |
| // | |
| // Free the allocated temp memory space. | |
| // | |
| FreePool (CapsuleGuidCache); | |
| FreePool (CapsulePtrCache); | |
| FreePool (CapsulePtr); | |
| return Status; | |
| } | |