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