--- /dev/null
+/** @file\r
+ The implementation supports Capusle on Disk.\r
+\r
+ Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>\r
+ SPDX-License-Identifier: BSD-2-Clause-Patent\r
+\r
+**/\r
+\r
+#include "CapsuleOnDisk.h"\r
+\r
+/**\r
+ Return if this capsule is a capsule name capsule, based upon CapsuleHeader.\r
+\r
+ @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER\r
+\r
+ @retval TRUE It is a capsule name capsule.\r
+ @retval FALSE It is not a capsule name capsule.\r
+**/\r
+BOOLEAN\r
+IsCapsuleNameCapsule (\r
+ IN EFI_CAPSULE_HEADER *CapsuleHeader\r
+ );\r
+\r
+/**\r
+ Check the integrity of the capsule name capsule.\r
+ If the capsule is vaild, return the physical address of each capsule name string.\r
+\r
+ @param[in] CapsuleHeader Pointer to the capsule header of a capsule name capsule.\r
+ @param[out] CapsuleNameNum Number of capsule name.\r
+\r
+ @retval NULL Capsule name capsule is not valid.\r
+ @retval CapsuleNameBuf Array of capsule name physical address.\r
+\r
+**/\r
+EFI_PHYSICAL_ADDRESS *\r
+ValidateCapsuleNameCapsuleIntegrity (\r
+ IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
+ OUT UINTN *CapsuleNameNum\r
+ )\r
+{\r
+ UINT8 *CapsuleNamePtr;\r
+ UINT8 *CapsuleNameBufStart;\r
+ UINT8 *CapsuleNameBufEnd;\r
+ UINTN Index;\r
+ UINTN StringSize;\r
+ EFI_PHYSICAL_ADDRESS *CapsuleNameBuf;\r
+\r
+ if (!IsCapsuleNameCapsule (CapsuleHeader)) {\r
+ return NULL;\r
+ }\r
+\r
+ //\r
+ // Total string size must be even.\r
+ //\r
+ if (((CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize) & BIT0) != 0) {\r
+ return NULL;\r
+ }\r
+\r
+ *CapsuleNameNum = 0;\r
+ Index = 0;\r
+ CapsuleNameBufStart = (UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize;\r
+\r
+ //\r
+ // If strings are not aligned on a 16-bit boundary, reallocate memory for it.\r
+ //\r
+ if (((UINTN) CapsuleNameBufStart & BIT0) != 0) {\r
+ CapsuleNameBufStart = AllocateCopyPool (CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize, CapsuleNameBufStart);\r
+ }\r
+\r
+ CapsuleNameBufEnd = CapsuleNameBufStart + CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize;\r
+\r
+ CapsuleNamePtr = CapsuleNameBufStart;\r
+ while (CapsuleNamePtr < CapsuleNameBufEnd) {\r
+ StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));\r
+ CapsuleNamePtr += StringSize;\r
+ (*CapsuleNameNum) ++;\r
+ }\r
+\r
+ //\r
+ // Integrity check.\r
+ //\r
+ if (CapsuleNamePtr != CapsuleNameBufEnd) {\r
+ if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {\r
+ FreePool (CapsuleNameBufStart);\r
+ }\r
+ return NULL;\r
+ }\r
+\r
+ CapsuleNameBuf = AllocatePool (*CapsuleNameNum * sizeof (EFI_PHYSICAL_ADDRESS));\r
+ if (CapsuleNameBuf == NULL) {\r
+ if (CapsuleNameBufStart != (UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize) {\r
+ FreePool (CapsuleNameBufStart);\r
+ }\r
+ return NULL;\r
+ }\r
+\r
+ CapsuleNamePtr = CapsuleNameBufStart;\r
+ while (CapsuleNamePtr < CapsuleNameBufEnd) {\r
+ StringSize= StrnSizeS ((CHAR16 *) CapsuleNamePtr, (CapsuleNameBufEnd - CapsuleNamePtr)/sizeof(CHAR16));\r
+ CapsuleNameBuf[Index] = (EFI_PHYSICAL_ADDRESS)(UINTN) CapsuleNamePtr;\r
+ CapsuleNamePtr += StringSize;\r
+ Index ++;\r
+ }\r
+\r
+ return CapsuleNameBuf;\r
+}\r
+\r
+/**\r
+ This routine is called to upper case given unicode string.\r
+\r
+ @param[in] Str String to upper case\r
+\r
+ @retval upper cased string after process\r
+\r
+**/\r
+static\r
+CHAR16 *\r
+UpperCaseString (\r
+ IN CHAR16 *Str\r
+ )\r
+{\r
+ CHAR16 *Cptr;\r
+\r
+ for (Cptr = Str; *Cptr; Cptr++) {\r
+ if (L'a' <= *Cptr && *Cptr <= L'z') {\r
+ *Cptr = *Cptr - L'a' + L'A';\r
+ }\r
+ }\r
+\r
+ return Str;\r
+}\r
+\r
+/**\r
+ This routine is used to return substring before period '.' or '\0'\r
+ Caller should respsonsible of substr space allocation & free\r
+\r
+ @param[in] Str String to check\r
+ @param[out] SubStr First part of string before period or '\0'\r
+ @param[out] SubStrLen Length of first part of string\r
+\r
+**/\r
+static\r
+VOID\r
+GetSubStringBeforePeriod (\r
+ IN CHAR16 *Str,\r
+ OUT CHAR16 *SubStr,\r
+ OUT UINTN *SubStrLen\r
+ )\r
+{\r
+ UINTN Index;\r
+ for (Index = 0; Str[Index] != L'.' && Str[Index] != L'\0'; Index++) {\r
+ SubStr[Index] = Str[Index];\r
+ }\r
+\r
+ SubStr[Index] = L'\0';\r
+ *SubStrLen = Index;\r
+}\r
+\r
+/**\r
+ This routine pad the string in tail with input character.\r
+\r
+ @param[in] StrBuf Str buffer to be padded, should be enough room for\r
+ @param[in] PadLen Expected padding length\r
+ @param[in] Character Character used to pad\r
+\r
+**/\r
+static\r
+VOID\r
+PadStrInTail (\r
+ IN CHAR16 *StrBuf,\r
+ IN UINTN PadLen,\r
+ IN CHAR16 Character\r
+ )\r
+{\r
+ UINTN Index;\r
+\r
+ for (Index = 0; StrBuf[Index] != L'\0'; Index++);\r
+\r
+ while(PadLen != 0) {\r
+ StrBuf[Index] = Character;\r
+ Index++;\r
+ PadLen--;\r
+ }\r
+\r
+ StrBuf[Index] = L'\0';\r
+}\r
+\r
+/**\r
+ This routine find the offset of the last period '.' of string. If No period exists\r
+ function FileNameExtension is set to L'\0'\r
+\r
+ @param[in] FileName File name to split between last period\r
+ @param[out] FileNameFirst First FileName before last period\r
+ @param[out] FileNameExtension FileName after last period\r
+\r
+**/\r
+static\r
+VOID\r
+SplitFileNameExtension (\r
+ IN CHAR16 *FileName,\r
+ OUT CHAR16 *FileNameFirst,\r
+ OUT CHAR16 *FileNameExtension\r
+ )\r
+{\r
+ UINTN Index;\r
+ UINTN StringLen;\r
+\r
+ StringLen = StrnLenS(FileName, MAX_FILE_NAME_SIZE);\r
+ for (Index = StringLen; Index > 0 && FileName[Index] != L'.'; Index--);\r
+\r
+ //\r
+ // No period exists. No FileName Extension\r
+ //\r
+ if (Index == 0 && FileName[Index] != L'.') {\r
+ FileNameExtension[0] = L'\0';\r
+ Index = StringLen;\r
+ } else {\r
+ StrCpyS(FileNameExtension, MAX_FILE_NAME_SIZE, &FileName[Index+1]);\r
+ }\r
+\r
+ //\r
+ // Copy First file name\r
+ //\r
+ StrnCpyS(FileNameFirst, MAX_FILE_NAME_SIZE, FileName, Index);\r
+ FileNameFirst[Index] = L'\0';\r
+}\r
+\r
+/**\r
+ This routine is called to get all boot options in the order determnined by:\r
+ 1. "OptionBuf"\r
+ 2. "BootOrder"\r
+\r
+ @param[out] OptionBuf BootList buffer to all boot options returned\r
+ @param[out] OptionCount BootList count of all boot options returned\r
+\r
+ @retval EFI_SUCCESS There is no error when processing capsule\r
+\r
+**/\r
+EFI_STATUS\r
+GetBootOptionInOrder(\r
+ OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf,\r
+ OUT UINTN *OptionCount\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN DataSize;\r
+ UINT16 BootNext;\r
+ CHAR16 BootOptionName[20];\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOrderOptionBuf;\r
+ UINTN BootOrderCount;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION BootNextOptionEntry;\r
+ UINTN BootNextCount;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *TempBuf;\r
+\r
+ BootOrderOptionBuf = NULL;\r
+ TempBuf = NULL;\r
+ BootNextCount = 0;\r
+ BootOrderCount = 0;\r
+ *OptionBuf = NULL;\r
+ *OptionCount = 0;\r
+\r
+ //\r
+ // First Get BootOption from "BootNext"\r
+ //\r
+ DataSize = sizeof(BootNext);\r
+ Status = gRT->GetVariable (\r
+ EFI_BOOT_NEXT_VARIABLE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ NULL,\r
+ &DataSize,\r
+ (VOID *)&BootNext\r
+ );\r
+ //\r
+ // BootNext variable is a single UINT16\r
+ //\r
+ if (!EFI_ERROR(Status) && DataSize == sizeof(UINT16)) {\r
+ //\r
+ // Add the boot next boot option\r
+ //\r
+ UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", BootNext);\r
+ ZeroMem(&BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));\r
+ Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOptionEntry);\r
+\r
+ if (!EFI_ERROR(Status)) {\r
+ BootNextCount = 1;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Second get BootOption from "BootOrder"\r
+ //\r
+ BootOrderOptionBuf = EfiBootManagerGetLoadOptions (&BootOrderCount, LoadOptionTypeBoot);\r
+ if (BootNextCount == 0 && BootOrderCount == 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // At least one BootOption is found\r
+ //\r
+ TempBuf = AllocatePool(sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * (BootNextCount + BootOrderCount));\r
+ if (TempBuf != NULL) {\r
+ if (BootNextCount == 1) {\r
+ CopyMem(TempBuf, &BootNextOptionEntry, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION));\r
+ }\r
+\r
+ if (BootOrderCount > 0) {\r
+ CopyMem(TempBuf + BootNextCount, BootOrderOptionBuf, sizeof(EFI_BOOT_MANAGER_LOAD_OPTION) * BootOrderCount);\r
+ }\r
+\r
+ *OptionBuf = TempBuf;\r
+ *OptionCount = BootNextCount + BootOrderCount;\r
+ Status = EFI_SUCCESS;\r
+ } else {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ FreePool(BootOrderOptionBuf);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This routine is called to get boot option by OptionNumber.\r
+\r
+ @param[in] Number The OptionNumber of boot option\r
+ @param[out] OptionBuf BootList buffer to all boot options returned\r
+\r
+ @retval EFI_SUCCESS There is no error when getting boot option\r
+\r
+**/\r
+EFI_STATUS\r
+GetBootOptionByNumber(\r
+ IN UINT16 Number,\r
+ OUT EFI_BOOT_MANAGER_LOAD_OPTION **OptionBuf\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 BootOptionName[20];\r
+ EFI_BOOT_MANAGER_LOAD_OPTION BootOption;\r
+\r
+ UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", Number);\r
+ ZeroMem (&BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
+ Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootOption);\r
+\r
+ if (!EFI_ERROR (Status)) {\r
+ *OptionBuf = AllocatePool (sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
+ CopyMem (*OptionBuf, &BootOption, sizeof (EFI_BOOT_MANAGER_LOAD_OPTION));\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Get Active EFI System Partition within GPT based on device path.\r
+\r
+ @param[in] DevicePath Device path to find a active EFI System Partition\r
+ @param[out] FsHandle BootList points to all boot options returned\r
+\r
+ @retval EFI_SUCCESS Active EFI System Partition is succesfully found\r
+ @retval EFI_NOT_FOUND No Active EFI System Partition is found\r
+\r
+**/\r
+EFI_STATUS\r
+GetEfiSysPartitionFromDevPath(\r
+ IN EFI_DEVICE_PATH_PROTOCOL *DevicePath,\r
+ OUT EFI_HANDLE *FsHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;\r
+ HARDDRIVE_DEVICE_PATH *Hd;\r
+ EFI_HANDLE Handle;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+\r
+ //\r
+ // Check if the device path contains GPT node\r
+ //\r
+ TempDevicePath = DevicePath;\r
+ while (!IsDevicePathEnd (TempDevicePath)) {\r
+ if ((DevicePathType (TempDevicePath) == MEDIA_DEVICE_PATH) &&\r
+ (DevicePathSubType (TempDevicePath) == MEDIA_HARDDRIVE_DP)) {\r
+ Hd = (HARDDRIVE_DEVICE_PATH *)TempDevicePath;\r
+ if (Hd->MBRType == MBR_TYPE_EFI_PARTITION_TABLE_HEADER) {\r
+ break;\r
+ }\r
+ }\r
+ TempDevicePath = NextDevicePathNode (TempDevicePath);\r
+ }\r
+\r
+ if (!IsDevicePathEnd (TempDevicePath)) {\r
+ //\r
+ // Search for EFI system partition protocol on full device path in Boot Option\r
+ //\r
+ Status = gBS->LocateDevicePath (&gEfiPartTypeSystemPartGuid, &DevicePath, &Handle);\r
+\r
+ //\r
+ // Search for simple file system on this handler\r
+ //\r
+ if (!EFI_ERROR(Status)) {\r
+ Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+ if (!EFI_ERROR(Status)) {\r
+ *FsHandle = Handle;\r
+ return EFI_SUCCESS;\r
+ }\r
+ }\r
+ }\r
+\r
+ return EFI_NOT_FOUND;\r
+}\r
+\r
+/**\r
+ This routine is called to get Simple File System protocol on the first EFI system partition found in\r
+ active boot option. The boot option list is detemined in order by\r
+ 1. "BootNext"\r
+ 2. "BootOrder"\r
+\r
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
+ device like USB can get enumerated.\r
+ @param[in, out] LoadOptionNumber On input, specify the boot option to get EFI system partition.\r
+ On output, return the OptionNumber of the boot option where EFI\r
+ system partition is got from.\r
+ @param[out] FsFsHandle Simple File System Protocol found on first active EFI system partition\r
+\r
+ @retval EFI_SUCCESS Simple File System protocol found for EFI system partition\r
+ @retval EFI_NOT_FOUND No Simple File System protocol found for EFI system partition\r
+\r
+**/\r
+EFI_STATUS\r
+GetEfiSysPartitionFromActiveBootOption(\r
+ IN UINTN MaxRetry,\r
+ IN OUT UINT16 **LoadOptionNumber,\r
+ OUT EFI_HANDLE *FsHandle\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionBuf;\r
+ UINTN BootOptionNum;\r
+ UINTN Index;\r
+ EFI_DEVICE_PATH_PROTOCOL *DevicePath;\r
+ EFI_DEVICE_PATH_PROTOCOL *CurFullPath;\r
+ EFI_DEVICE_PATH_PROTOCOL *PreFullPath;\r
+\r
+ *FsHandle = NULL;\r
+\r
+ if (*LoadOptionNumber != NULL) {\r
+ BootOptionNum = 1;\r
+ Status = GetBootOptionByNumber(**LoadOptionNumber, &BootOptionBuf);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "GetBootOptionByIndex Failed %x! No BootOption available for connection\n", Status));\r
+ return Status;\r
+ }\r
+ } else {\r
+ Status = GetBootOptionInOrder(&BootOptionBuf, &BootOptionNum);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "GetBootOptionInOrder Failed %x! No BootOption available for connection\n", Status));\r
+ return Status;\r
+ }\r
+ }\r
+\r
+ //\r
+ // Search BootOptionList to check if it is an active boot option with EFI system partition\r
+ // 1. Connect device path\r
+ // 2. expend short/plug in devicepath\r
+ // 3. LoadImage\r
+ //\r
+ for (Index = 0; Index < BootOptionNum; Index++) {\r
+ //\r
+ // Get the boot option from the link list\r
+ //\r
+ DevicePath = BootOptionBuf[Index].FilePath;\r
+\r
+ //\r
+ // Skip inactive or legacy boot options\r
+ //\r
+ if ((BootOptionBuf[Index].Attributes & LOAD_OPTION_ACTIVE) == 0 ||\r
+ DevicePathType (DevicePath) == BBS_DEVICE_PATH) {\r
+ continue;\r
+ }\r
+\r
+ DEBUG_CODE (\r
+ CHAR16 *DevicePathStr;\r
+\r
+ DevicePathStr = ConvertDevicePathToText(DevicePath, TRUE, TRUE);\r
+ if (DevicePathStr != NULL){\r
+ DEBUG((DEBUG_INFO, "Try BootOption %s\n", DevicePathStr));\r
+ FreePool(DevicePathStr);\r
+ } else {\r
+ DEBUG((DEBUG_INFO, "DevicePathToStr failed\n"));\r
+ }\r
+ );\r
+\r
+ CurFullPath = NULL;\r
+ //\r
+ // Try every full device Path generated from bootoption\r
+ //\r
+ do {\r
+ PreFullPath = CurFullPath;\r
+ CurFullPath = EfiBootManagerGetNextLoadOptionDevicePath(DevicePath, CurFullPath);\r
+\r
+ if (PreFullPath != NULL) {\r
+ FreePool (PreFullPath);\r
+ }\r
+\r
+ if (CurFullPath == NULL) {\r
+ //\r
+ // No Active EFI system partition is found in BootOption device path\r
+ //\r
+ Status = EFI_NOT_FOUND;\r
+ break;\r
+ }\r
+\r
+ DEBUG_CODE (\r
+ CHAR16 *DevicePathStr1;\r
+\r
+ DevicePathStr1 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);\r
+ if (DevicePathStr1 != NULL){\r
+ DEBUG((DEBUG_INFO, "Full device path %s\n", DevicePathStr1));\r
+ FreePool(DevicePathStr1);\r
+ }\r
+ );\r
+\r
+ //\r
+ // Make sure the boot option device path connected.\r
+ // Only handle first device in boot option. Other optional device paths are described as OSV specific\r
+ // FullDevice could contain extra directory & file info. So don't check connection status here.\r
+ //\r
+ EfiBootManagerConnectDevicePath (CurFullPath, NULL);\r
+ Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);\r
+\r
+ //\r
+ // Some relocation device like USB need more time to get enumerated\r
+ //\r
+ while (EFI_ERROR(Status) && MaxRetry > 0) {\r
+ EfiBootManagerConnectDevicePath(CurFullPath, NULL);\r
+\r
+ //\r
+ // Search for EFI system partition protocol on full device path in Boot Option\r
+ //\r
+ Status = GetEfiSysPartitionFromDevPath(CurFullPath, FsHandle);\r
+ if (!EFI_ERROR(Status)) {\r
+ break;\r
+ }\r
+ DEBUG((DEBUG_ERROR, "GetEfiSysPartitionFromDevPath Loop %x\n", Status));\r
+ //\r
+ // Stall 100ms if connection failed to ensure USB stack is ready\r
+ //\r
+ gBS->Stall(100000);\r
+ MaxRetry --;\r
+ }\r
+ } while(EFI_ERROR(Status));\r
+\r
+ //\r
+ // Find a qualified Simple File System\r
+ //\r
+ if (!EFI_ERROR(Status)) {\r
+ break;\r
+ }\r
+\r
+ }\r
+\r
+ //\r
+ // Return the OptionNumber of the boot option where EFI system partition is got from\r
+ //\r
+ if (*LoadOptionNumber == NULL) {\r
+ *LoadOptionNumber = AllocateCopyPool (sizeof(UINT16), (UINT16 *) &BootOptionBuf[Index].OptionNumber);\r
+ }\r
+\r
+ //\r
+ // No qualified EFI system partition found\r
+ //\r
+ if (*FsHandle == NULL) {\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+\r
+ DEBUG_CODE (\r
+ CHAR16 *DevicePathStr2;\r
+ if (*FsHandle != NULL) {\r
+ DevicePathStr2 = ConvertDevicePathToText(CurFullPath, TRUE, TRUE);\r
+ if (DevicePathStr2 != NULL){\r
+ DEBUG((DEBUG_INFO, "Found Active EFI System Partion on %s\n", DevicePathStr2));\r
+ FreePool(DevicePathStr2);\r
+ }\r
+ } else {\r
+ DEBUG((DEBUG_INFO, "Failed to found Active EFI System Partion\n"));\r
+ }\r
+ );\r
+\r
+ if (CurFullPath != NULL) {\r
+ FreePool(CurFullPath);\r
+ }\r
+\r
+ //\r
+ // Free BootOption Buffer\r
+ //\r
+ for (Index = 0; Index < BootOptionNum; Index++) {\r
+ if (BootOptionBuf[Index].Description != NULL) {\r
+ FreePool(BootOptionBuf[Index].Description);\r
+ }\r
+\r
+ if (BootOptionBuf[Index].FilePath != NULL) {\r
+ FreePool(BootOptionBuf[Index].FilePath);\r
+ }\r
+\r
+ if (BootOptionBuf[Index].OptionalData != NULL) {\r
+ FreePool(BootOptionBuf[Index].OptionalData);\r
+ }\r
+ }\r
+\r
+ FreePool(BootOptionBuf);\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This routine is called to get all file infos with in a given dir & with given file attribute, the file info is listed in\r
+ alphabetical order described in UEFI spec.\r
+\r
+ @param[in] Dir Directory file handler\r
+ @param[in] FileAttr Attribute of file to be red from directory\r
+ @param[out] FileInfoList File images info list red from directory\r
+ @param[out] FileNum File images number red from directory\r
+\r
+ @retval EFI_SUCCESS File FileInfo list in the given\r
+\r
+**/\r
+EFI_STATUS\r
+GetFileInfoListInAlphabetFromDir(\r
+ IN EFI_FILE_HANDLE Dir,\r
+ IN UINT64 FileAttr,\r
+ OUT LIST_ENTRY *FileInfoList,\r
+ OUT UINTN *FileNum\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ FILE_INFO_ENTRY *NewFileInfoEntry;\r
+ FILE_INFO_ENTRY *TempFileInfoEntry;\r
+ EFI_FILE_INFO *FileInfo;\r
+ CHAR16 *NewFileName;\r
+ CHAR16 *ListedFileName;\r
+ CHAR16 *NewFileNameExtension;\r
+ CHAR16 *ListedFileNameExtension;\r
+ CHAR16 *TempNewSubStr;\r
+ CHAR16 *TempListedSubStr;\r
+ LIST_ENTRY *Link;\r
+ BOOLEAN NoFile;\r
+ UINTN FileCount;\r
+ UINTN IndexNew;\r
+ UINTN IndexListed;\r
+ UINTN NewSubStrLen;\r
+ UINTN ListedSubStrLen;\r
+ INTN SubStrCmpResult;\r
+\r
+ Status = EFI_SUCCESS;\r
+ NewFileName = NULL;\r
+ ListedFileName = NULL;\r
+ NewFileNameExtension = NULL;\r
+ ListedFileNameExtension = NULL;\r
+ TempNewSubStr = NULL;\r
+ TempListedSubStr = NULL;\r
+ NoFile = FALSE;\r
+ FileCount = 0;\r
+\r
+ InitializeListHead(FileInfoList);\r
+\r
+ TempNewSubStr = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
+ TempListedSubStr = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
+\r
+ if (TempNewSubStr == NULL || TempListedSubStr == NULL ) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+\r
+ for ( Status = FileHandleFindFirstFile(Dir, &FileInfo)\r
+ ; !EFI_ERROR(Status) && !NoFile\r
+ ; Status = FileHandleFindNextFile(Dir, FileInfo, &NoFile)\r
+ ){\r
+\r
+ //\r
+ // Skip file with mismatching File attribute\r
+ //\r
+ if ((FileInfo->Attribute & (FileAttr)) == 0) {\r
+ continue;\r
+ }\r
+\r
+ NewFileInfoEntry = NULL;\r
+ NewFileInfoEntry = (FILE_INFO_ENTRY*)AllocateZeroPool(sizeof(FILE_INFO_ENTRY));\r
+ if (NewFileInfoEntry == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+ NewFileInfoEntry->Signature = FILE_INFO_SIGNATURE;\r
+ NewFileInfoEntry->FileInfo = AllocateCopyPool((UINTN) FileInfo->Size, FileInfo);\r
+ if (NewFileInfoEntry->FileInfo == NULL) {\r
+ FreePool(NewFileInfoEntry);\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+\r
+ NewFileInfoEntry->FileNameFirstPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
+ if (NewFileInfoEntry->FileNameFirstPart == NULL) {\r
+ FreePool(NewFileInfoEntry->FileInfo);\r
+ FreePool(NewFileInfoEntry);\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+ NewFileInfoEntry->FileNameSecondPart = (CHAR16 *) AllocateZeroPool(MAX_FILE_NAME_SIZE);\r
+ if (NewFileInfoEntry->FileNameSecondPart == NULL) {\r
+ FreePool(NewFileInfoEntry->FileInfo);\r
+ FreePool(NewFileInfoEntry->FileNameFirstPart);\r
+ FreePool(NewFileInfoEntry);\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Splitter the whole New file name into 2 parts between the last period L'.' into NewFileName NewFileExtension\r
+ // If no period in the whole file name. NewFileExtension is set to L'\0'\r
+ //\r
+ NewFileName = NewFileInfoEntry->FileNameFirstPart;\r
+ NewFileNameExtension = NewFileInfoEntry->FileNameSecondPart;\r
+ SplitFileNameExtension(FileInfo->FileName, NewFileName, NewFileNameExtension);\r
+ UpperCaseString(NewFileName);\r
+ UpperCaseString(NewFileNameExtension);\r
+\r
+ //\r
+ // Insert capsule file in alphabetical ordered list\r
+ //\r
+ for (Link = FileInfoList->ForwardLink; Link != FileInfoList; Link = Link->ForwardLink) {\r
+ //\r
+ // Get the FileInfo from the link list\r
+ //\r
+ TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+ ListedFileName = TempFileInfoEntry->FileNameFirstPart;\r
+ ListedFileNameExtension = TempFileInfoEntry->FileNameSecondPart;\r
+\r
+ //\r
+ // Follow rule in UEFI spec 8.5.5 to compare file name\r
+ //\r
+ IndexListed = 0;\r
+ IndexNew = 0;\r
+ while (TRUE){\r
+ //\r
+ // First compare each substrings in NewFileName & ListedFileName between periods\r
+ //\r
+ GetSubStringBeforePeriod(&NewFileName[IndexNew], TempNewSubStr, &NewSubStrLen);\r
+ GetSubStringBeforePeriod(&ListedFileName[IndexListed], TempListedSubStr, &ListedSubStrLen);\r
+ if (NewSubStrLen > ListedSubStrLen) {\r
+ //\r
+ // Substr in NewFileName is longer. Pad tail with SPACE\r
+ //\r
+ PadStrInTail(TempListedSubStr, NewSubStrLen - ListedSubStrLen, L' ');\r
+ } else if (NewSubStrLen < ListedSubStrLen){\r
+ //\r
+ // Substr in ListedFileName is longer. Pad tail with SPACE\r
+ //\r
+ PadStrInTail(TempNewSubStr, ListedSubStrLen - NewSubStrLen, L' ');\r
+ }\r
+\r
+ SubStrCmpResult = StrnCmp(TempNewSubStr, TempListedSubStr, MAX_FILE_NAME_LEN);\r
+ if (SubStrCmpResult != 0) {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Move to skip this substring\r
+ //\r
+ IndexNew += NewSubStrLen;\r
+ IndexListed += ListedSubStrLen;\r
+ //\r
+ // Reach File First Name end\r
+ //\r
+ if (NewFileName[IndexNew] == L'\0' || ListedFileName[IndexListed] == L'\0') {\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Skip the period L'.'\r
+ //\r
+ IndexNew++;\r
+ IndexListed++;\r
+ }\r
+\r
+ if (SubStrCmpResult < 0) {\r
+ //\r
+ // NewFileName is smaller. Find the right place to insert New file\r
+ //\r
+ break;\r
+ } else if (SubStrCmpResult == 0) {\r
+ //\r
+ // 2 cases whole NewFileName is smaller than ListedFileName\r
+ // 1. if NewFileName == ListedFileName. Continue to compare FileNameExtension\r
+ // 2. if NewFileName is shorter than ListedFileName\r
+ //\r
+ if (NewFileName[IndexNew] == L'\0') {\r
+ if (ListedFileName[IndexListed] != L'\0' || (StrnCmp(NewFileNameExtension, ListedFileNameExtension, MAX_FILE_NAME_LEN) < 0)) {\r
+ break;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Other case, ListedFileName is smaller. Continue to compare the next file in the list\r
+ //\r
+ }\r
+\r
+ //\r
+ // If Find an entry in the list whose name is bigger than new FileInfo in alphabet order\r
+ // Insert it before this entry\r
+ // else\r
+ // Insert at the tail of this list (Link = FileInfoList)\r
+ //\r
+ InsertTailList(Link, &NewFileInfoEntry->Link);\r
+\r
+ FileCount++;\r
+ }\r
+\r
+ *FileNum = FileCount;\r
+\r
+EXIT:\r
+\r
+ if (TempNewSubStr != NULL) {\r
+ FreePool(TempNewSubStr);\r
+ }\r
+\r
+ if (TempListedSubStr != NULL) {\r
+ FreePool(TempListedSubStr);\r
+ }\r
+\r
+ if (EFI_ERROR(Status)) {\r
+ while(!IsListEmpty(FileInfoList)) {\r
+ Link = FileInfoList->ForwardLink;\r
+ RemoveEntryList(Link);\r
+\r
+ TempFileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+\r
+ FreePool(TempFileInfoEntry->FileInfo);\r
+ FreePool(TempFileInfoEntry->FileNameFirstPart);\r
+ FreePool(TempFileInfoEntry->FileNameSecondPart);\r
+ FreePool(TempFileInfoEntry);\r
+ }\r
+ *FileNum = 0;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ This routine is called to get all qualified image from file from an given directory\r
+ in alphabetic order. All the file image is copied to allocated boottime memory.\r
+ Caller should free these memory\r
+\r
+ @param[in] Dir Directory file handler\r
+ @param[in] FileAttr Attribute of file to be red from directory\r
+ @param[out] FilePtr File images Info buffer red from directory\r
+ @param[out] FileNum File images number red from directory\r
+\r
+ @retval EFI_SUCCESS Succeed to get all capsules in alphabetic order.\r
+\r
+**/\r
+EFI_STATUS\r
+GetFileImageInAlphabetFromDir(\r
+ IN EFI_FILE_HANDLE Dir,\r
+ IN UINT64 FileAttr,\r
+ OUT IMAGE_INFO **FilePtr,\r
+ OUT UINTN *FileNum\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Link;\r
+ EFI_FILE_HANDLE FileHandle;\r
+ FILE_INFO_ENTRY *FileInfoEntry;\r
+ EFI_FILE_INFO *FileInfo;\r
+ UINTN FileCount;\r
+ IMAGE_INFO *TempFilePtrBuf;\r
+ UINTN Size;\r
+ LIST_ENTRY FileInfoList;\r
+\r
+ FileHandle = NULL;\r
+ FileCount = 0;\r
+ TempFilePtrBuf = NULL;\r
+ *FilePtr = NULL;\r
+\r
+ //\r
+ // Get file list in Dir in alphabetical order\r
+ //\r
+ Status = GetFileInfoListInAlphabetFromDir(\r
+ Dir,\r
+ FileAttr,\r
+ &FileInfoList,\r
+ &FileCount\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));\r
+ goto EXIT;\r
+ }\r
+\r
+ if (FileCount == 0) {\r
+ DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));\r
+ Status = EFI_NOT_FOUND;\r
+ goto EXIT;\r
+ }\r
+\r
+ TempFilePtrBuf = (IMAGE_INFO *)AllocateZeroPool(sizeof(IMAGE_INFO) * FileCount);\r
+ if (TempFilePtrBuf == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Read all files from FileInfoList to BS memory\r
+ //\r
+ FileCount = 0;\r
+ for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {\r
+ //\r
+ // Get FileInfo from the link list\r
+ //\r
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+ FileInfo = FileInfoEntry->FileInfo;\r
+\r
+ Status = Dir->Open(\r
+ Dir,\r
+ &FileHandle,\r
+ FileInfo->FileName,\r
+ EFI_FILE_MODE_READ,\r
+ 0\r
+ );\r
+ if (EFI_ERROR(Status)){\r
+ continue;\r
+ }\r
+\r
+ Size = (UINTN)FileInfo->FileSize;\r
+ TempFilePtrBuf[FileCount].ImageAddress = AllocateZeroPool(Size);\r
+ if (TempFilePtrBuf[FileCount].ImageAddress == NULL) {\r
+ DEBUG((DEBUG_ERROR, "Fail to allocate memory for capsule. Stop processing the rest.\n"));\r
+ break;\r
+ }\r
+\r
+ Status = FileHandle->Read(\r
+ FileHandle,\r
+ &Size,\r
+ TempFilePtrBuf[FileCount].ImageAddress\r
+ );\r
+\r
+ FileHandle->Close(FileHandle);\r
+\r
+ //\r
+ // Skip read error file\r
+ //\r
+ if (EFI_ERROR(Status) || Size != (UINTN)FileInfo->FileSize) {\r
+ //\r
+ // Remove this error file info accordingly\r
+ // & move Link to BackLink\r
+ //\r
+ Link = RemoveEntryList(Link);\r
+ Link = Link->BackLink;\r
+\r
+ FreePool(FileInfoEntry->FileInfo);\r
+ FreePool(FileInfoEntry->FileNameFirstPart);\r
+ FreePool(FileInfoEntry->FileNameSecondPart);\r
+ FreePool(FileInfoEntry);\r
+\r
+ FreePool(TempFilePtrBuf[FileCount].ImageAddress);\r
+ TempFilePtrBuf[FileCount].ImageAddress = NULL;\r
+ TempFilePtrBuf[FileCount].FileInfo = NULL;\r
+\r
+ continue;\r
+ }\r
+ TempFilePtrBuf[FileCount].FileInfo = FileInfo;\r
+ FileCount++;\r
+ }\r
+\r
+ DEBUG_CODE (\r
+ for (Link = FileInfoList.ForwardLink; Link != &FileInfoList; Link = Link->ForwardLink) {\r
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+ FileInfo = FileInfoEntry->FileInfo;\r
+ DEBUG((DEBUG_INFO, "Successfully read capsule file %s from disk.\n", FileInfo->FileName));\r
+ }\r
+ );\r
+\r
+EXIT:\r
+\r
+ *FilePtr = TempFilePtrBuf;\r
+ *FileNum = FileCount;\r
+\r
+ //\r
+ // FileInfo will be freed by Calller\r
+ //\r
+ while(!IsListEmpty(&FileInfoList)) {\r
+ Link = FileInfoList.ForwardLink;\r
+ RemoveEntryList(Link);\r
+\r
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+\r
+ FreePool(FileInfoEntry->FileNameFirstPart);\r
+ FreePool(FileInfoEntry->FileNameSecondPart);\r
+ FreePool(FileInfoEntry);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This routine is called to remove all qualified image from file from an given directory.\r
+\r
+ @param[in] Dir Directory file handler\r
+ @param[in] FileAttr Attribute of files to be deleted\r
+\r
+ @retval EFI_SUCCESS Succeed to remove all files from an given directory.\r
+\r
+**/\r
+EFI_STATUS\r
+RemoveFileFromDir(\r
+ IN EFI_FILE_HANDLE Dir,\r
+ IN UINT64 FileAttr\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ LIST_ENTRY *Link;\r
+ LIST_ENTRY FileInfoList;\r
+ EFI_FILE_HANDLE FileHandle;\r
+ FILE_INFO_ENTRY *FileInfoEntry;\r
+ EFI_FILE_INFO *FileInfo;\r
+ UINTN FileCount;\r
+\r
+ FileHandle = NULL;\r
+\r
+ //\r
+ // Get file list in Dir in alphabetical order\r
+ //\r
+ Status = GetFileInfoListInAlphabetFromDir(\r
+ Dir,\r
+ FileAttr,\r
+ &FileInfoList,\r
+ &FileCount\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "GetFileInfoListInAlphabetFromDir Failed!\n"));\r
+ goto EXIT;\r
+ }\r
+\r
+ if (FileCount == 0) {\r
+ DEBUG ((DEBUG_ERROR, "No file found in Dir!\n"));\r
+ Status = EFI_NOT_FOUND;\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Delete all files with given attribute in Dir\r
+ //\r
+ for (Link = FileInfoList.ForwardLink; Link != &(FileInfoList); Link = Link->ForwardLink) {\r
+ //\r
+ // Get FileInfo from the link list\r
+ //\r
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+ FileInfo = FileInfoEntry->FileInfo;\r
+\r
+ Status = Dir->Open(\r
+ Dir,\r
+ &FileHandle,\r
+ FileInfo->FileName,\r
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
+ 0\r
+ );\r
+ if (EFI_ERROR(Status)){\r
+ continue;\r
+ }\r
+\r
+ Status = FileHandle->Delete(FileHandle);\r
+ }\r
+\r
+EXIT:\r
+\r
+ while(!IsListEmpty(&FileInfoList)) {\r
+ Link = FileInfoList.ForwardLink;\r
+ RemoveEntryList(Link);\r
+\r
+ FileInfoEntry = CR (Link, FILE_INFO_ENTRY, Link, FILE_INFO_SIGNATURE);\r
+\r
+ FreePool(FileInfoEntry->FileInfo);\r
+ FreePool(FileInfoEntry);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This routine is called to get all caspules from file. The capsule file image is\r
+ copied to BS memory. Caller is responsible to free them.\r
+\r
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
+ devices like USB can get enumerated.\r
+ @param[out] CapsulePtr Copied Capsule file Image Info buffer\r
+ @param[out] CapsuleNum CapsuleNumber\r
+ @param[out] FsHandle File system handle\r
+ @param[out] LoadOptionNumber OptionNumber of boot option\r
+\r
+ @retval EFI_SUCCESS Succeed to get all capsules.\r
+\r
+**/\r
+EFI_STATUS\r
+GetAllCapsuleOnDisk(\r
+ IN UINTN MaxRetry,\r
+ OUT IMAGE_INFO **CapsulePtr,\r
+ OUT UINTN *CapsuleNum,\r
+ OUT EFI_HANDLE *FsHandle,\r
+ OUT UINT16 *LoadOptionNumber\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+ EFI_FILE_HANDLE RootDir;\r
+ EFI_FILE_HANDLE FileDir;\r
+ UINT16 *TempOptionNumber;\r
+\r
+ Fs = NULL;\r
+ RootDir = NULL;\r
+ FileDir = NULL;\r
+ TempOptionNumber = NULL;\r
+ *CapsuleNum = 0;\r
+\r
+ Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &TempOptionNumber, FsHandle);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = gBS->HandleProtocol(*FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = Fs->OpenVolume(Fs, &RootDir);\r
+ if (EFI_ERROR(Status)) {\r
+ return Status;\r
+ }\r
+\r
+ Status = RootDir->Open(\r
+ RootDir,\r
+ &FileDir,\r
+ EFI_CAPSULE_FILE_DIRECTORY,\r
+ EFI_FILE_MODE_READ,\r
+ 0\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG((DEBUG_ERROR, "CodLibGetAllCapsuleOnDisk fail to open RootDir!\n"));\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Only Load files with EFI_FILE_SYSTEM or EFI_FILE_ARCHIVE attribute\r
+ // ignore EFI_FILE_READ_ONLY, EFI_FILE_HIDDEN, EFI_FILE_RESERVED, EFI_FILE_DIRECTORY\r
+ //\r
+ Status = GetFileImageInAlphabetFromDir(\r
+ FileDir,\r
+ EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE,\r
+ CapsulePtr,\r
+ CapsuleNum\r
+ );\r
+ DEBUG((DEBUG_INFO, "GetFileImageInAlphabetFromDir status %x\n", Status));\r
+\r
+ //\r
+ // Always remove file to avoid deadloop in capsule process\r
+ //\r
+ Status = RemoveFileFromDir(FileDir, EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE);\r
+ DEBUG((DEBUG_INFO, "RemoveFileFromDir status %x\n", Status));\r
+\r
+ if (LoadOptionNumber != NULL) {\r
+ *LoadOptionNumber = *TempOptionNumber;\r
+ }\r
+\r
+EXIT:\r
+\r
+ if (FileDir != NULL) {\r
+ FileDir->Close (FileDir);\r
+ }\r
+\r
+ if (RootDir != NULL) {\r
+ RootDir->Close (RootDir);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Build Gather list for a list of capsule images.\r
+\r
+ @param[in] CapsuleBuffer An array of pointer to capsule images\r
+ @param[in] CapsuleSize An array of UINTN to capsule images size\r
+ @param[in] CapsuleNum The count of capsule images\r
+ @param[out] BlockDescriptors The block descriptors for the capsule images\r
+\r
+ @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.\r
+\r
+**/\r
+EFI_STATUS\r
+BuildGatherList (\r
+ IN VOID **CapsuleBuffer,\r
+ IN UINTN *CapsuleSize,\r
+ IN UINTN CapsuleNum,\r
+ OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors1;\r
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorPre;\r
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorsHeader;\r
+ UINTN Index;\r
+\r
+ BlockDescriptors1 = NULL;\r
+ BlockDescriptorPre = NULL;\r
+ BlockDescriptorsHeader = NULL;\r
+\r
+ for (Index = 0; Index < CapsuleNum; Index++) {\r
+ //\r
+ // Allocate memory for the descriptors.\r
+ //\r
+ BlockDescriptors1 = AllocateZeroPool (2 * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR));\r
+ if (BlockDescriptors1 == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "BuildGatherList: failed to allocate memory for descriptors\n"));\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto ERREXIT;\r
+ } else {\r
+ DEBUG ((DEBUG_INFO, "BuildGatherList: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1));\r
+ }\r
+\r
+ //\r
+ // Record descirptor header\r
+ //\r
+ if (Index == 0) {\r
+ BlockDescriptorsHeader = BlockDescriptors1;\r
+ }\r
+\r
+ if (BlockDescriptorPre != NULL) {\r
+ BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1;\r
+ BlockDescriptorPre->Length = 0;\r
+ }\r
+\r
+ BlockDescriptors1->Union.DataBlock = (UINTN) CapsuleBuffer[Index];\r
+ BlockDescriptors1->Length = CapsuleSize[Index];\r
+\r
+ BlockDescriptorPre = BlockDescriptors1 + 1;\r
+ BlockDescriptors1 = NULL;\r
+ }\r
+\r
+ //\r
+ // Null-terminate.\r
+ //\r
+ if (BlockDescriptorPre != NULL) {\r
+ BlockDescriptorPre->Union.ContinuationPointer = (UINTN)NULL;\r
+ BlockDescriptorPre->Length = 0;\r
+ *BlockDescriptors = BlockDescriptorsHeader;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+\r
+ERREXIT:\r
+ if (BlockDescriptors1 != NULL) {\r
+ FreePool (BlockDescriptors1);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ This routine is called to check if CapsuleOnDisk flag in OsIndications Variable\r
+ is enabled.\r
+\r
+ @retval TRUE Flag is enabled\r
+ @retval FALSE Flag is not enabled\r
+\r
+**/\r
+BOOLEAN\r
+EFIAPI\r
+CoDCheckCapsuleOnDiskFlag(\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 OsIndication;\r
+ UINTN DataSize;\r
+\r
+ //\r
+ // Check File Capsule Delivery Supported Flag in OsIndication variable\r
+ //\r
+ OsIndication = 0;\r
+ DataSize = sizeof(UINT64);\r
+ Status = gRT->GetVariable (\r
+ EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ NULL,\r
+ &DataSize,\r
+ &OsIndication\r
+ );\r
+ if (!EFI_ERROR(Status) &&\r
+ (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) {\r
+ return TRUE;\r
+ }\r
+\r
+ return FALSE;\r
+}\r
+\r
+\r
+/**\r
+ This routine is called to clear CapsuleOnDisk flags including OsIndications and BootNext variable.\r
+\r
+ @retval EFI_SUCCESS All Capsule On Disk flags are cleared\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDClearCapsuleOnDiskFlag(\r
+ VOID\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINT64 OsIndication;\r
+ UINTN DataSize;\r
+\r
+ //\r
+ // Reset File Capsule Delivery Supported Flag in OsIndication variable\r
+ //\r
+ OsIndication = 0;\r
+ DataSize = sizeof(UINT64);\r
+ Status = gRT->GetVariable (\r
+ EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ NULL,\r
+ &DataSize,\r
+ &OsIndication\r
+ );\r
+ if (EFI_ERROR(Status) ||\r
+ (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) == 0) {\r
+ return Status;\r
+ }\r
+\r
+ OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED);\r
+ Status = gRT->SetVariable (\r
+ EFI_OS_INDICATIONS_VARIABLE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE,\r
+ sizeof(UINT64),\r
+ &OsIndication\r
+ );\r
+ ASSERT(!EFI_ERROR(Status));\r
+\r
+ //\r
+ // Delete BootNext variable. Capsule Process may reset system, so can't rely on Bds to clear this variable\r
+ //\r
+ Status = gRT->SetVariable (\r
+ EFI_BOOT_NEXT_VARIABLE_NAME,\r
+ &gEfiGlobalVariableGuid,\r
+ 0,\r
+ 0,\r
+ NULL\r
+ );\r
+ ASSERT (Status == EFI_SUCCESS || Status == EFI_NOT_FOUND);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+ This routine is called to clear CapsuleOnDisk Relocation Info variable.\r
+ Total Capsule On Disk length is recorded in this variable\r
+\r
+ @retval EFI_SUCCESS Capsule On Disk flags are cleared\r
+\r
+**/\r
+EFI_STATUS\r
+CoDClearCapsuleRelocationInfo(\r
+ VOID\r
+ )\r
+{\r
+ return gRT->SetVariable (\r
+ COD_RELOCATION_INFO_VAR_NAME,\r
+ &gEfiCapsuleVendorGuid,\r
+ 0,\r
+ 0,\r
+ NULL\r
+ );\r
+}\r
+\r
+/**\r
+ Relocate Capsule on Disk from EFI system partition to a platform-specific NV storage device\r
+ with BlockIo protocol. Relocation device path, identified by PcdCodRelocationDevPath, must\r
+ be a full device path.\r
+ Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+ Function will stall 100ms between each retry.\r
+\r
+ Side Effects:\r
+ Content corruption. Block IO write directly touches low level write. Orignal partitions, file systems\r
+ of the relocation device will be corrupted.\r
+\r
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
+ devices like USB can get enumerated.\r
+\r
+ @retval EFI_SUCCESS Capsule on Disk images are sucessfully relocated to the platform-specific device.\r
+\r
+**/\r
+EFI_STATUS\r
+RelocateCapsuleToDisk(\r
+ UINTN MaxRetry\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN CapsuleOnDiskNum;\r
+ UINTN Index;\r
+ UINTN DataSize;\r
+ UINT64 TotalImageSize;\r
+ UINT64 TotalImageNameSize;\r
+ IMAGE_INFO *CapsuleOnDiskBuf;\r
+ EFI_HANDLE Handle;\r
+ EFI_HANDLE TempHandle;\r
+ EFI_HANDLE *HandleBuffer;\r
+ UINTN NumberOfHandles;\r
+ EFI_BLOCK_IO_PROTOCOL *BlockIo;\r
+ UINT8 *CapsuleDataBuf;\r
+ UINT8 *CapsulePtr;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+ EFI_FILE_HANDLE RootDir;\r
+ EFI_FILE_HANDLE TempCodFile;\r
+ UINT64 TempCodFileSize;\r
+ EFI_DEVICE_PATH *TempDevicePath;\r
+ BOOLEAN RelocationInfo;\r
+ UINT16 LoadOptionNumber;\r
+ EFI_CAPSULE_HEADER FileNameCapsuleHeader;\r
+\r
+ RootDir = NULL;\r
+ TempCodFile = NULL;\r
+ HandleBuffer = NULL;\r
+ CapsuleDataBuf = NULL;\r
+ CapsuleOnDiskBuf = NULL;\r
+ NumberOfHandles = 0;\r
+\r
+ DEBUG ((DEBUG_INFO, "CapsuleOnDisk RelocateCapsule Enter\n"));\r
+\r
+ //\r
+ // 1. Load all Capsule On Disks in to memory\r
+ //\r
+ Status = GetAllCapsuleOnDisk(MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, &LoadOptionNumber);\r
+ if (EFI_ERROR(Status) || CapsuleOnDiskNum == 0) {\r
+ DEBUG ((DEBUG_INFO, "RelocateCapsule: GetAllCapsuleOnDisk Status - 0x%x\n", Status));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // 2. Connect platform special device path as relocation device.\r
+ // If no platform special device path specified or the device path is invalid, use the EFI system partition where\r
+ // stores the capsules as relocation device.\r
+ //\r
+ if (IsDevicePathValid ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), PcdGetSize(PcdCodRelocationDevPath))) {\r
+ Status = EfiBootManagerConnectDevicePath ((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), &TempHandle);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "RelocateCapsule: EfiBootManagerConnectDevicePath Status - 0x%x\n", Status));\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Connect all the child handle. Partition & FAT drivers are allowed in this case\r
+ //\r
+ gBS->ConnectController (TempHandle, NULL, NULL, TRUE);\r
+ Status = gBS->LocateHandleBuffer(\r
+ ByProtocol,\r
+ &gEfiSimpleFileSystemProtocolGuid,\r
+ NULL,\r
+ &NumberOfHandles,\r
+ &HandleBuffer\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG ((DEBUG_ERROR, "RelocateCapsule: LocateHandleBuffer Status - 0x%x\n", Status));\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Find first Simple File System Handle which can match PcdCodRelocationDevPath\r
+ //\r
+ for (Index = 0; Index < NumberOfHandles; Index++) {\r
+ Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiDevicePathProtocolGuid, (VOID **)&TempDevicePath);\r
+ if (EFI_ERROR(Status)) {\r
+ continue;\r
+ }\r
+\r
+ DataSize = GetDevicePathSize((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath)) - sizeof(EFI_DEVICE_PATH);\r
+ if (0 == CompareMem((EFI_DEVICE_PATH *)PcdGetPtr(PcdCodRelocationDevPath), TempDevicePath, DataSize)) {\r
+ Handle = HandleBuffer[Index];\r
+ break;\r
+ }\r
+ }\r
+\r
+ FreePool(HandleBuffer);\r
+\r
+ if (Index == NumberOfHandles) {\r
+ DEBUG ((DEBUG_ERROR, "RelocateCapsule: No simple file system protocol found.\n"));\r
+ Status = EFI_NOT_FOUND;\r
+ }\r
+ }\r
+\r
+ Status = gBS->HandleProtocol(Handle, &gEfiBlockIoProtocolGuid, (VOID **)&BlockIo);\r
+ if (EFI_ERROR(Status) || BlockIo->Media->ReadOnly) {\r
+ DEBUG((DEBUG_ERROR, "Fail to find Capsule on Disk relocation BlockIo device or device is ReadOnly!\n"));\r
+ goto EXIT;\r
+ }\r
+\r
+ Status = gBS->HandleProtocol(Handle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+ if (EFI_ERROR(Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Check if device used to relocate Capsule On Disk is big enough\r
+ //\r
+ TotalImageSize = 0;\r
+ TotalImageNameSize = 0;\r
+ for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
+ //\r
+ // Overflow check\r
+ //\r
+ if (MAX_ADDRESS - (UINTN)TotalImageSize <= CapsuleOnDiskBuf[Index].FileInfo->FileSize) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto EXIT;\r
+ }\r
+\r
+ if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName)) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto EXIT;\r
+ }\r
+\r
+ TotalImageSize += CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
+ TotalImageNameSize += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
+ DEBUG((DEBUG_INFO, "RelocateCapsule: %x Size %x\n",CapsuleOnDiskBuf[Index].FileInfo->FileName, CapsuleOnDiskBuf[Index].FileInfo->FileSize));\r
+ }\r
+\r
+ DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageSize %x\n", TotalImageSize));\r
+ DEBUG((DEBUG_INFO, "RelocateCapsule: TotalImageNameSize %x\n", TotalImageNameSize));\r
+\r
+ if (MAX_ADDRESS - (UINTN)TotalImageNameSize <= sizeof(UINT64) * 2 ||\r
+ MAX_ADDRESS - (UINTN)TotalImageSize <= (UINTN)TotalImageNameSize + sizeof(UINT64) * 2) {\r
+ Status = EFI_INVALID_PARAMETER;\r
+ goto EXIT;\r
+ }\r
+\r
+ TempCodFileSize = sizeof(UINT64) + TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;\r
+\r
+ //\r
+ // Check if CapsuleTotalSize. There could be reminder, so use LastBlock number directly\r
+ //\r
+ if (DivU64x32(TempCodFileSize, BlockIo->Media->BlockSize) > BlockIo->Media->LastBlock) {\r
+ DEBUG((DEBUG_ERROR, "RelocateCapsule: Relocation device isn't big enough to hold all Capsule on Disk!\n"));\r
+ DEBUG((DEBUG_ERROR, "TotalImageSize = %x\n", TotalImageSize));\r
+ DEBUG((DEBUG_ERROR, "TotalImageNameSize = %x\n", TotalImageNameSize));\r
+ DEBUG((DEBUG_ERROR, "RelocationDev BlockSize = %x LastBlock = %x\n", BlockIo->Media->BlockSize, BlockIo->Media->LastBlock));\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+\r
+ CapsuleDataBuf = AllocatePool((UINTN) TempCodFileSize);\r
+ if (CapsuleDataBuf == NULL) {\r
+ Status = EFI_OUT_OF_RESOURCES;\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // First UINT64 reserved for total image size, including capsule name capsule.\r
+ //\r
+ *(UINT64 *) CapsuleDataBuf = TotalImageSize + sizeof(EFI_CAPSULE_HEADER) + TotalImageNameSize;\r
+\r
+ //\r
+ // Line up all the Capsule on Disk and write to relocation disk at one time. It could save some time in disk write\r
+ //\r
+ for (Index = 0, CapsulePtr = CapsuleDataBuf + sizeof(UINT64); Index < CapsuleOnDiskNum; Index++) {\r
+ CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].ImageAddress, (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize);\r
+ CapsulePtr += CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
+ }\r
+\r
+ //\r
+ // Line the capsule header for capsule name capsule.\r
+ //\r
+ CopyGuid(&FileNameCapsuleHeader.CapsuleGuid, &gEdkiiCapsuleOnDiskNameGuid);\r
+ FileNameCapsuleHeader.CapsuleImageSize = (UINT32) TotalImageNameSize + sizeof(EFI_CAPSULE_HEADER);\r
+ FileNameCapsuleHeader.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;\r
+ FileNameCapsuleHeader.HeaderSize = sizeof(EFI_CAPSULE_HEADER);\r
+ CopyMem(CapsulePtr, &FileNameCapsuleHeader, FileNameCapsuleHeader.HeaderSize);\r
+ CapsulePtr += FileNameCapsuleHeader.HeaderSize;\r
+\r
+ //\r
+ // Line up all the Capsule file names.\r
+ //\r
+ for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
+ CopyMem(CapsulePtr, CapsuleOnDiskBuf[Index].FileInfo->FileName, StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName));\r
+ CapsulePtr += StrSize(CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
+ }\r
+\r
+ //\r
+ // 5. Flash all Capsules on Disk to TempCoD.tmp under RootDir\r
+ //\r
+ Status = Fs->OpenVolume(Fs, &RootDir);\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG((DEBUG_ERROR, "RelocateCapsule: OpenVolume error. %x\n", Status));\r
+ goto EXIT;\r
+ }\r
+\r
+ Status = RootDir->Open(\r
+ RootDir,\r
+ &TempCodFile,\r
+ (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
+ 0\r
+ );\r
+ if (!EFI_ERROR(Status)) {\r
+ //\r
+ // Error handling code to prevent malicious code to hold this file to block capsule on disk\r
+ //\r
+ TempCodFile->Delete(TempCodFile);\r
+ }\r
+ Status = RootDir->Open(\r
+ RootDir,\r
+ &TempCodFile,\r
+ (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE,\r
+ 0\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG((DEBUG_ERROR, "RelocateCapsule: Open TemCoD.tmp error. %x\n", Status));\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Always write at the begining of TempCap file\r
+ //\r
+ DataSize = (UINTN) TempCodFileSize;\r
+ Status = TempCodFile->Write(\r
+ TempCodFile,\r
+ &DataSize,\r
+ CapsuleDataBuf\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ DEBUG((DEBUG_ERROR, "RelocateCapsule: Write TemCoD.tmp error. %x\n", Status));\r
+ goto EXIT;\r
+ }\r
+\r
+ if (DataSize != TempCodFileSize) {\r
+ Status = EFI_DEVICE_ERROR;\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Save Capsule On Disk relocation info to "CodRelocationInfo" Var\r
+ // It is used in next reboot by TCB\r
+ //\r
+ RelocationInfo = TRUE;\r
+ Status = gRT->SetVariable(\r
+ COD_RELOCATION_INFO_VAR_NAME,\r
+ &gEfiCapsuleVendorGuid,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ sizeof (BOOLEAN),\r
+ &RelocationInfo\r
+ );\r
+ //\r
+ // Save the LoadOptionNumber of the boot option, where the capsule is relocated,\r
+ // into "CodRelocationLoadOption" var. It is used in next reboot after capsule is\r
+ // updated out of TCB to remove the TempCoDFile.\r
+ //\r
+ Status = gRT->SetVariable(\r
+ COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
+ &gEfiCapsuleVendorGuid,\r
+ EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
+ sizeof (UINT16),\r
+ &LoadOptionNumber\r
+ );\r
+\r
+EXIT:\r
+\r
+ if (CapsuleDataBuf != NULL) {\r
+ FreePool(CapsuleDataBuf);\r
+ }\r
+\r
+ if (CapsuleOnDiskBuf != NULL) {\r
+ //\r
+ // Free resources allocated by CodLibGetAllCapsuleOnDisk\r
+ //\r
+ for (Index = 0; Index < CapsuleOnDiskNum; Index++ ) {\r
+ FreePool(CapsuleOnDiskBuf[Index].ImageAddress);\r
+ FreePool(CapsuleOnDiskBuf[Index].FileInfo);\r
+ }\r
+ FreePool(CapsuleOnDiskBuf);\r
+ }\r
+\r
+ if (TempCodFile != NULL) {\r
+ if (EFI_ERROR(Status)) {\r
+ TempCodFile->Delete (TempCodFile);\r
+ } else {\r
+ TempCodFile->Close (TempCodFile);\r
+ }\r
+ }\r
+\r
+ if (RootDir != NULL) {\r
+ RootDir->Close (RootDir);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ For the platforms that support Capsule In Ram, reuse the Capsule In Ram to deliver capsule.\r
+ Relocate Capsule On Disk to memory and call UpdateCapsule().\r
+ Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+ Function will stall 100ms between each retry.\r
+\r
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
+ devices like USB can get enumerated.\r
+\r
+ @retval EFI_SUCCESS Deliver capsule through Capsule In Ram successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+RelocateCapsuleToRam (\r
+ UINTN MaxRetry\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN CapsuleOnDiskNum;\r
+ IMAGE_INFO *CapsuleOnDiskBuf;\r
+ EFI_HANDLE Handle;\r
+ EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;\r
+ VOID **CapsuleBuffer;\r
+ UINTN *CapsuleSize;\r
+ EFI_CAPSULE_HEADER *FileNameCapsule;\r
+ UINTN Index;\r
+ UINT8 *StringBuf;\r
+ UINTN StringSize;\r
+ UINTN TotalStringSize;\r
+\r
+ CapsuleOnDiskBuf = NULL;\r
+ BlockDescriptors = NULL;\r
+ CapsuleBuffer = NULL;\r
+ CapsuleSize = NULL;\r
+ FileNameCapsule = NULL;\r
+ TotalStringSize = 0;\r
+\r
+ //\r
+ // 1. Load all Capsule On Disks into memory\r
+ //\r
+ Status = GetAllCapsuleOnDisk (MaxRetry, &CapsuleOnDiskBuf, &CapsuleOnDiskNum, &Handle, NULL);\r
+ if (EFI_ERROR (Status) || CapsuleOnDiskNum == 0) {\r
+ DEBUG ((DEBUG_ERROR, "GetAllCapsuleOnDisk Status - 0x%x\n", Status));\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // 2. Add a capsule for Capsule file name strings\r
+ //\r
+ CapsuleBuffer = AllocateZeroPool ((CapsuleOnDiskNum + 1) * sizeof (VOID *));\r
+ if (CapsuleBuffer == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ CapsuleSize = AllocateZeroPool ((CapsuleOnDiskNum + 1) * sizeof (UINTN));\r
+ if (CapsuleSize == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "Fail to allocate memory for capsules.\n"));\r
+ FreePool (CapsuleBuffer);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ for (Index = 0; Index < CapsuleOnDiskNum; Index++) {\r
+ CapsuleBuffer[Index] = (VOID *)(UINTN) CapsuleOnDiskBuf[Index].ImageAddress;\r
+ CapsuleSize[Index] = (UINTN) CapsuleOnDiskBuf[Index].FileInfo->FileSize;\r
+ TotalStringSize += StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
+ }\r
+\r
+ FileNameCapsule = AllocateZeroPool (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);\r
+ if (FileNameCapsule == NULL) {\r
+ DEBUG ((DEBUG_ERROR, "Fail to allocate memory for name capsule.\n"));\r
+ FreePool (CapsuleBuffer);\r
+ FreePool (CapsuleSize);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ FileNameCapsule->CapsuleImageSize = (UINT32) (sizeof (EFI_CAPSULE_HEADER) + TotalStringSize);\r
+ FileNameCapsule->Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;\r
+ FileNameCapsule->HeaderSize = sizeof (EFI_CAPSULE_HEADER);\r
+ CopyGuid (&(FileNameCapsule->CapsuleGuid), &gEdkiiCapsuleOnDiskNameGuid);\r
+\r
+ StringBuf = (UINT8 *)FileNameCapsule + FileNameCapsule->HeaderSize;\r
+ for (Index = 0; Index < CapsuleOnDiskNum; Index ++) {\r
+ StringSize = StrSize (CapsuleOnDiskBuf[Index].FileInfo->FileName);\r
+ CopyMem (StringBuf, CapsuleOnDiskBuf[Index].FileInfo->FileName, StringSize);\r
+ StringBuf += StringSize;\r
+ }\r
+\r
+ CapsuleBuffer[CapsuleOnDiskNum] = FileNameCapsule;\r
+ CapsuleSize[CapsuleOnDiskNum] = TotalStringSize + sizeof (EFI_CAPSULE_HEADER);\r
+\r
+ //\r
+ // 3. Build Gather list for the capsules\r
+ //\r
+ Status = BuildGatherList (CapsuleBuffer, CapsuleSize, CapsuleOnDiskNum + 1, &BlockDescriptors);\r
+ if (EFI_ERROR (Status) || BlockDescriptors == NULL) {\r
+ FreePool (CapsuleBuffer);\r
+ FreePool (CapsuleSize);\r
+ FreePool (FileNameCapsule);\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // 4. Call UpdateCapsule() service\r
+ //\r
+ Status = gRT->UpdateCapsule((EFI_CAPSULE_HEADER **) CapsuleBuffer, CapsuleOnDiskNum + 1, (UINTN) BlockDescriptors);\r
+\r
+ return Status;\r
+}\r
+\r
+/**\r
+ Relocate Capsule on Disk from EFI system partition.\r
+\r
+ Two solution to deliver Capsule On Disk:\r
+ Solution A: If PcdCapsuleInRamSupport is enabled, relocate Capsule On Disk to memory and call UpdateCapsule().\r
+ Solution B: If PcdCapsuleInRamSupport is disabled, relocate Capsule On Disk to a platform-specific NV storage\r
+ device with BlockIo protocol.\r
+\r
+ Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+ Function will stall 100ms between each retry.\r
+\r
+ Side Effects:\r
+ Capsule Delivery Supported Flag in OsIndication variable and BootNext variable will be cleared.\r
+ Solution B: Content corruption. Block IO write directly touches low level write. Orignal partitions, file\r
+ systems of the relocation device will be corrupted.\r
+\r
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
+ devices like USB can get enumerated. Input 0 means no retry.\r
+\r
+ @retval EFI_SUCCESS Capsule on Disk images are successfully relocated.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDRelocateCapsule(\r
+ UINTN MaxRetry\r
+ )\r
+{\r
+ if (!PcdGetBool (PcdCapsuleOnDiskSupport)) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Clear CapsuleOnDisk Flag firstly.\r
+ //\r
+ CoDClearCapsuleOnDiskFlag ();\r
+\r
+ //\r
+ // If Capsule In Ram is supported, delivery capsules through memory\r
+ //\r
+ if (PcdGetBool (PcdCapsuleInRamSupport)) {\r
+ DEBUG ((DEBUG_INFO, "Capsule In Ram is supported, call gRT->UpdateCapsule().\n"));\r
+ return RelocateCapsuleToRam (MaxRetry);\r
+ } else {\r
+ DEBUG ((DEBUG_INFO, "Reallcoate all Capsule on Disks to %s in RootDir.\n", (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName)));\r
+ return RelocateCapsuleToDisk (MaxRetry);\r
+ }\r
+}\r
+\r
+/**\r
+ Remove the temp file from the root of EFI System Partition.\r
+ Device enumeration like USB costs time, user can input MaxRetry to tell function to retry.\r
+ Function will stall 100ms between each retry.\r
+\r
+ @param[in] MaxRetry Max Connection Retry. Stall 100ms between each connection try to ensure\r
+ devices like USB can get enumerated. Input 0 means no retry.\r
+\r
+ @retval EFI_SUCCESS Remove the temp file successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+CoDRemoveTempFile (\r
+ UINTN MaxRetry\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN DataSize;\r
+ UINT16 *LoadOptionNumber;\r
+ EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs;\r
+ EFI_HANDLE FsHandle;\r
+ EFI_FILE_HANDLE RootDir;\r
+ EFI_FILE_HANDLE TempCodFile;\r
+\r
+ RootDir = NULL;\r
+ TempCodFile = NULL;\r
+ DataSize = sizeof(UINT16);\r
+\r
+ LoadOptionNumber = AllocatePool (sizeof(UINT16));\r
+ if (LoadOptionNumber == NULL) {\r
+ return EFI_OUT_OF_RESOURCES;\r
+ }\r
+\r
+ //\r
+ // Check if capsule files are relocated\r
+ //\r
+ Status = gRT->GetVariable (\r
+ COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
+ &gEfiCapsuleVendorGuid,\r
+ NULL,\r
+ &DataSize,\r
+ (VOID *)LoadOptionNumber\r
+ );\r
+ if (EFI_ERROR(Status) || DataSize != sizeof(UINT16)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Get the EFI file system from the boot option where the capsules are relocated\r
+ //\r
+ Status = GetEfiSysPartitionFromActiveBootOption(MaxRetry, &LoadOptionNumber, &FsHandle);\r
+ if (EFI_ERROR(Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ Status = gBS->HandleProtocol(FsHandle, &gEfiSimpleFileSystemProtocolGuid, (VOID **)&Fs);\r
+ if (EFI_ERROR(Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ Status = Fs->OpenVolume(Fs, &RootDir);\r
+ if (EFI_ERROR(Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ //\r
+ // Delete the TempCoDFile\r
+ //\r
+ Status = RootDir->Open(\r
+ RootDir,\r
+ &TempCodFile,\r
+ (CHAR16 *)PcdGetPtr(PcdCoDRelocationFileName),\r
+ EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,\r
+ 0\r
+ );\r
+ if (EFI_ERROR(Status)) {\r
+ goto EXIT;\r
+ }\r
+\r
+ TempCodFile->Delete(TempCodFile);\r
+\r
+ //\r
+ // Clear "CoDRelocationLoadOption" variable\r
+ //\r
+ Status = gRT->SetVariable (\r
+ COD_RELOCATION_LOAD_OPTION_VAR_NAME,\r
+ &gEfiCapsuleVendorGuid,\r
+ 0,\r
+ 0,\r
+ NULL\r
+ );\r
+\r
+EXIT:\r
+ if (LoadOptionNumber != NULL) {\r
+ FreePool (LoadOptionNumber);\r
+ }\r
+\r
+ if (RootDir != NULL) {\r
+ RootDir->Close(RootDir);\r
+ }\r
+\r
+ return Status;\r
+}\r