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