From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received-SPF: Pass (sender SPF authorized) identity=mailfrom; client-ip=192.55.52.151; helo=mga17.intel.com; envelope-from=chen.a.chen@intel.com; receiver=edk2-devel@lists.01.org Received: from mga17.intel.com (mga17.intel.com [192.55.52.151]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ml01.01.org (Postfix) with ESMTPS id 5EDD3211B85E3 for ; Thu, 24 Jan 2019 22:14:03 -0800 (PST) X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from fmsmga002.fm.intel.com ([10.253.24.26]) by fmsmga107.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 24 Jan 2019 22:14:02 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.56,520,1539673200"; d="scan'208";a="137647000" Received: from chenche4.ccr.corp.intel.com ([10.239.9.12]) by fmsmga002.fm.intel.com with ESMTP; 24 Jan 2019 22:14:01 -0800 From: Chen A Chen To: edk2-devel@lists.01.org Cc: Chen A Chen , Jian J Wang , Hao Wu , Zhang Chao B Date: Fri, 25 Jan 2019 14:13:59 +0800 Message-Id: <20190125061359.8788-1-chen.a.chen@intel.com> X-Mailer: git-send-email 2.16.2.windows.1 Subject: [PATCH 3/3] MdeModulePkg/CapsuleApp: Enhance CapsuleApp to support Capsule-on-Disk. X-BeenThere: edk2-devel@lists.01.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: EDK II Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 25 Jan 2019 06:14:03 -0000 BZ:https://bugzilla.tianocore.org/show_bug.cgi?id=1482 CapsuleApp is used for trigger capsule update. Add -OD option in CapsuleApp to support doing capsule update via storage. Add -F and -L options to support dumping information feature. Cc: Jian J Wang Cc: Hao Wu Cc: Zhang Chao B Contributed-under: TianoCore Contribution Agreement 1.1 Signed-off-by: Chen A Chen --- MdeModulePkg/Application/CapsuleApp/CapsuleApp.c | 103 ++- MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf | 8 + MdeModulePkg/Application/CapsuleApp/CapsuleDump.c | 535 +++++++++++++- .../Application/CapsuleApp/CapsuleOnDisk.c | 802 +++++++++++++++++++++ MdeModulePkg/Include/Library/UefiBootManagerLib.h | 19 + MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c | 23 + 6 files changed, 1487 insertions(+), 3 deletions(-) create mode 100644 MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c index acae0fe261..522ba40311 100644 --- a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c +++ b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ UINTN NumberOfDescriptors = 1; STATIC CONST SHELL_PARAM_ITEM ParamList[] = { {L"-C", TypeFlag}, {L"-E", TypeFlag}, + {L"-F", TypeFlag}, {L"-S", TypeFlag}, {L"-NR", TypeFlag}, @@ -58,6 +60,9 @@ STATIC CONST SHELL_PARAM_ITEM ParamList[] = { {L"-D", TypeValue}, {L"-P", TypeValue}, {L"-I", TypeValue}, + {L"-L", TypeValue}, + + {L"-OD", TypeValue}, {NULL, TypeMax} }; @@ -118,6 +123,44 @@ DumpEsrtData ( VOID ); +/** + Dump Provisioned Capsule. + + @param[in] DumpCapsuleInfo The flag to indicate whether to dump the capsule inforomation. +**/ +VOID +DumpProvisionedCapsule ( + IN BOOLEAN DumpCapsuleInfo + ); + +/** + Dump all EFI System Parition. +**/ +VOID +DumpAllEfiSysPartition ( + VOID + ); + +/** + Process Capsule On Disk. + + @param[in] CapsuleBuffer An array of pointer to capsule images + @param[in] FileSize An array of UINTN to capsule images size + @param[in] OrgFileName An array of orginal capsule images name + @param[in] NewFileName An array of new capsule images name + @param[in] CapsuleNum The count of capsule images + + @retval EFI_SUCCESS Capsule on disk secceed. +**/ +EFI_STATUS +ProcessCapsuleOnDisk ( + IN VOID **CapsuleBuffer, + IN UINTN *CapsuleBufferSize, + IN CHAR16 *Map, + IN CHAR16 **OutputCapsuleNames, + IN UINTN CapsuleNum + ); + /** Read a file. @@ -786,11 +829,14 @@ PrintUsage ( ) { Print(L"CapsuleApp: usage\n"); - Print(L" CapsuleApp [-NR]\n"); + Print(L" CapsuleApp [-NR] [-OD [FS]]\n"); Print(L" CapsuleApp -S\n"); Print(L" CapsuleApp -C\n"); Print(L" CapsuleApp -P\n"); Print(L" CapsuleApp -E\n"); + Print(L" CapsuleApp -L\n"); + Print(L" CapsuleApp -L INFO\n"); + Print(L" CapsuleApp -F\n"); Print(L" CapsuleApp -G -O \n"); Print(L" CapsuleApp -N -O \n"); Print(L" CapsuleApp -D \n"); @@ -806,6 +852,8 @@ PrintUsage ( Print(L" -P: Dump UEFI FMP protocol info, or get image with specified\n"); Print(L" ImageTypeId and Index (decimal format) to a file\n"); Print(L" -E: Dump UEFI ESRT table info.\n"); + Print(L" -L: Dump provisioned capsule image information.\n"); + Print(L" -F: Dump all EFI System Partition.\n"); Print(L" -G: Convert a BMP file to be an UX capsule,\n"); Print(L" according to Windows Firmware Update document\n"); Print(L" -N: Append a Capsule Header to an existing FMP capsule image\n"); @@ -815,6 +863,7 @@ PrintUsage ( Print(L" -D: Dump Capsule image header information, image payload\n"); Print(L" information if it is an UX capsule and FMP header\n"); Print(L" information if it is a FMP capsule.\n"); + Print(L" -OD: Do update capsule via storage.\n"); } /** @@ -846,12 +895,16 @@ UefiMain ( CHAR16 *ProblemParam; CHAR16 *BmpFileName; CHAR16 *CapsuleFileName; + CHAR16 *CapsuleFileNames[MAX_CAPSULE_NUM]; CHAR16 *OutputFileName; CHAR16 *GuidStr; CHAR16 *IndexStr; + CHAR16 *DumpInfoStr; + CHAR16 *FsMapStr; EFI_GUID ImageTypeGuid; BOOLEAN NoResetFlag; BOOLEAN NeedReset; + BOOLEAN CapsuleOnDisk; UINTN CapsuleBufferSize[MAX_CAPSULE_NUM]; UINTN CapsuleNum; UINTN ImageIndex; @@ -868,7 +921,6 @@ UefiMain ( Status = ShellCommandLineParse (ParamList, &Package, &ProblemParam, TRUE); if (EFI_ERROR (Status)) { PrintUsage (); - Status = EFI_UNSUPPORTED; goto Done; } @@ -909,6 +961,27 @@ UefiMain ( DumpEsrtData (); } + // + // CapsuleApp -F + // + if (ShellCommandLineGetFlag (Package, L"-F")) { + DumpAllEfiSysPartition (); + } + + // + // CapsuleApp -L [Info] + // + if (ShellCommandLineGetFlag (Package, L"-L")) { + DumpInfoStr = (CHAR16 *)ShellCommandLineGetValue (Package, L"-L"); + if (DumpInfoStr == NULL) { + DumpProvisionedCapsule (FALSE); + } else { + if (StrCmp (DumpInfoStr, L"INFO") == 0) { + DumpProvisionedCapsule (TRUE); + } + } + } + // // CapsuleApp -P // @@ -981,6 +1054,9 @@ UefiMain ( } } } else { + // + // CapsuleApp Cap1 ... [-NR] [-OD [FSX:]] + // if (CapsuleNum > MAX_CAPSULE_NUM) { Print(L"CapsuleApp: Too many capsule images.\n"); Status = EFI_UNSUPPORTED; @@ -991,6 +1067,11 @@ UefiMain ( NoResetFlag = TRUE; } + if (ShellCommandLineGetFlag (Package, L"-OD")) { + CapsuleOnDisk = TRUE; + FsMapStr = (CHAR16 *)ShellCommandLineGetValue (Package, L"-OD"); + } + for (Index = 0; Index < CapsuleNum; Index++) { CapsuleFileName = (CHAR16 *)ShellCommandLineGetRawValue (Package, Index + 1); Status = ReadFileToBuffer (CapsuleFileName, &CapsuleBufferSize[Index], &CapsuleBuffer[Index]); @@ -1003,6 +1084,7 @@ UefiMain ( Status = EFI_INVALID_PARAMETER; goto Done; } + CapsuleFileNames[Index] = CapsuleFileName; } // @@ -1044,6 +1126,23 @@ UefiMain ( } } + // + // Check whether is capsule on disk. + // + if (CapsuleOnDisk) { + Status = ProcessCapsuleOnDisk (CapsuleBuffer, CapsuleBufferSize, FsMapStr, CapsuleFileNames, CapsuleNum); + if (EFI_ERROR (Status)) { + Print (L"CapsuleApp: failed to update capsule - %r\n", Status); + goto Done; + } else { + if (!NoResetFlag) { + gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL); + } else { + goto Done; + } + } + } + // // Check whether the input capsule image has the flag of persist across system reset. // diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf index d4edc539cc..3627fa788f 100644 --- a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf +++ b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf @@ -33,6 +33,7 @@ [Sources] CapsuleApp.c CapsuleDump.c + CapsuleOnDisk.c AppSupport.c [Packages] @@ -41,16 +42,20 @@ ShellPkg/ShellPkg.dec [Guids] + gEfiGlobalVariableGuid ## CONSUMES ## GUID gEfiCapsuleReportGuid ## CONSUMES ## GUID gEfiFmpCapsuleGuid ## CONSUMES ## GUID gWindowsUxCapsuleGuid ## CONSUMES ## GUID gEfiSystemResourceTableGuid ## CONSUMES ## GUID + gEfiCapsuleVendorGuid ## SOMETIMES_CONSUMES ## Variable:L"CapsuleUpdateData" + gEfiPartTypeSystemPartGuid ## SOMETIMES_CONSUMES ## GUID [Protocols] gEfiGraphicsOutputProtocolGuid ## CONSUMES gEfiFirmwareManagementProtocolGuid ## CONSUMES gEfiShellParametersProtocolGuid ## CONSUMES gEfiShellProtocolGuid ## CONSUMES + gEfiSimpleFileSystemProtocolGuid ## SOMETIMES_CONSUMES [LibraryClasses] BaseLib @@ -63,6 +68,9 @@ PrintLib BmpSupportLib ShellLib + FileHandleLib + UefiBootManagerLib + SortLib [UserExtensions.TianoCore."ExtraFiles"] CapsuleAppExtra.uni diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c b/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c index 7a3eb94362..2af28d711c 100644 --- a/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c +++ b/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c @@ -21,13 +21,26 @@ #include #include #include +#include +#include +#include +#include #include +#include +#include #include #include #include #include +#include #include +// +// (20 * (6+5+2))+1) unicode characters from EFI FAT spec (doubled for bytes) +// +#define MAX_FILE_NAME_SIZE 522 +#define MAX_FILE_NAME_LEN (MAX_FILE_NAME_SIZE / sizeof(CHAR16)) + /** Read a file. @@ -61,6 +74,37 @@ WriteFileFromBuffer ( IN VOID *Buffer ); +/** + Get shell protocol. + + @return Pointer to shell protocol. + +**/ +EFI_SHELL_PROTOCOL * +GetShellProtocol ( + VOID + ); + +/** + Get SimpleFileSystem from boot option file path + + @param[in] DevicePath The file path of boot option + @param[out] FullPath The full device path of boot device + @param[out] Fs The file system within EfiSysPartition + + @retval EFI_SUCCESS Get file system successfully + @retval EFI_NOT_FOUND No valid file system found + @retval others Get file system failed + +**/ +EFI_STATUS +EFIAPI +GetEfiSysPartitionFromBootOptionFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs + ); + /** Validate if it is valid capsule header @@ -123,7 +167,7 @@ DumpFmpCapsule ( UINTN Count; EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *FmpImageHeader; - Print(L"[FmpCapusule]\n"); + Print(L"[FmpCapsule]\n"); Print(L"CapsuleHeader:\n"); Print(L" CapsuleGuid - %g\n", &CapsuleHeader->CapsuleGuid); Print(L" HeaderSize - 0x%x\n", CapsuleHeader->HeaderSize); @@ -504,6 +548,495 @@ DumpEsrtData ( Print(L"\n"); } + +/** + Dump capsule information from CapsuleHeader + + @param[in] CapsuleHeader The CapsuleHeader of the capsule image. + + @retval EFI_SUCCESS The capsule information is dumped. + +**/ +EFI_STATUS +DumpCapsuleFromBuffer ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) { + DumpUxCapsule (CapsuleHeader); + return EFI_SUCCESS; + } + + if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)) { + DumpFmpCapsule (CapsuleHeader); + } + if (IsNestedFmpCapsule (CapsuleHeader)) { + Print (L"[NestedCapusule]\n"); + Print (L"CapsuleHeader:\n"); + Print (L" CapsuleGuid - %g\n", &CapsuleHeader->CapsuleGuid); + Print (L" HeaderSize - 0x%x\n", CapsuleHeader->HeaderSize); + Print (L" Flags - 0x%x\n", CapsuleHeader->Flags); + Print (L" CapsuleImageSize - 0x%x\n", CapsuleHeader->CapsuleImageSize); + DumpFmpCapsule ((EFI_CAPSULE_HEADER *)((UINTN)CapsuleHeader + CapsuleHeader->HeaderSize)); + } + + return EFI_SUCCESS; +} + +/** + 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; 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 = StrLen(FileName); + 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_LEN, &FileName[Index+1]); + } + + // + // Copy First file name + // + StrnCpyS (FileNameFirst, MAX_FILE_NAME_LEN, FileName, Index); + FileNameFirst[Index] = L'\0'; +} + +/** + The function is called by PerformQuickSort to sort file name in alphabet. + + @param[in] Left The pointer to first buffer. + @param[in] Right The pointer to second buffer. + + @retval 0 Buffer1 equal to Buffer2. + @return <0 Buffer1 is less than Buffer2. + @return >0 Buffer1 is greater than Buffer2. + +**/ +INTN +EFIAPI +CompareFileNameInAlphabet ( + IN EFI_PHYSICAL_ADDRESS *Left, + IN EFI_PHYSICAL_ADDRESS *Right + ) { + EFI_FILE_INFO *FileInfo1; + EFI_FILE_INFO *FileInfo2; + CHAR16 FileName1[MAX_FILE_NAME_SIZE]; + CHAR16 FileExtension1[MAX_FILE_NAME_SIZE]; + CHAR16 FileName2[MAX_FILE_NAME_SIZE]; + CHAR16 FileExtension2[MAX_FILE_NAME_SIZE]; + CHAR16 TempSubStr1[MAX_FILE_NAME_SIZE]; + CHAR16 TempSubStr2[MAX_FILE_NAME_SIZE]; + UINTN SubStrLen1; + UINTN SubStrLen2; + INTN SubStrCmpResult; + + FileInfo1 = (EFI_FILE_INFO *) *Left; + FileInfo2 = (EFI_FILE_INFO *) *Right; + + SplitFileNameExtension (FileInfo1->FileName, FileName1, FileExtension1); + SplitFileNameExtension (FileInfo2->FileName, FileName2, FileExtension2); + + UpperCaseString (FileName1); + UpperCaseString (FileName2); + + GetSubStringBeforePeriod (FileName1, TempSubStr1, &SubStrLen1); + GetSubStringBeforePeriod (FileName2, TempSubStr2, &SubStrLen2); + + if (SubStrLen1 > SubStrLen2) { + // + // Substr in NewFileName is longer. Pad tail with SPACE + // + PadStrInTail (TempSubStr2, SubStrLen1 - SubStrLen2, L' '); + } else if (SubStrLen1 < SubStrLen2){ + // + // Substr in ListedFileName is longer. Pad tail with SPACE + // + PadStrInTail (TempSubStr1, SubStrLen2 - SubStrLen1, L' '); + } + + SubStrCmpResult = StrnCmp (TempSubStr1, TempSubStr2, MAX_FILE_NAME_LEN); + if (SubStrCmpResult != 0) { + return SubStrCmpResult; + } + + UpperCaseString (FileExtension1); + UpperCaseString (FileExtension2); + + return StrnCmp (FileExtension1, FileExtension2, MAX_FILE_NAME_LEN); +} + +/** + Dump capsule information from disk + + @param[in] DevicePath The device path of disk. + @param[in] DumpCapsuleInfo The flag to indicate whether to dump the capsule inforomation. + + @retval EFI_SUCCESS The capsule information is dumped. + +**/ +EFI_STATUS +DumpCapsuleFromDisk ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs, + IN BOOLEAN DumpCapsuleInfo + ) +{ + EFI_STATUS Status; + EFI_FILE *Root; + EFI_FILE *DirHandle; + EFI_FILE *FileHandle; + UINTN Index; + UINTN FileSize; + VOID *FileBuffer; + EFI_FILE_INFO **FileInfoBuffer; + EFI_FILE_INFO *FileInfo; + UINTN FileCount; + BOOLEAN NoFile; + + DirHandle = NULL; + FileHandle = NULL; + Index = 0; + FileCount = 0; + NoFile = FALSE; + + Status = Fs->OpenVolume (Fs, &Root); + if (EFI_ERROR (Status)) { + Print (L"Cannot open volume. Status = %r\n", Status); + return EFI_NOT_FOUND; + } + + Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FROM_FILE_DIR, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0); + if (EFI_ERROR (Status)) { + Print (L"Cannot open %s. Status = %r\n", EFI_CAPSULE_FROM_FILE_DIR, Status); + return EFI_NOT_FOUND; + } + + // + // Get file count first + // + for ( Status = FileHandleFindFirstFile (DirHandle, &FileInfo) + ; !EFI_ERROR(Status) && !NoFile + ; Status = FileHandleFindNextFile (DirHandle, FileInfo, &NoFile) + ){ + if ((FileInfo->Attribute & (EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE)) == 0) { + continue; + } + FileCount ++; + } + + if (FileCount == 0) { + Print (L"Error: No capsule file found!\n"); + return EFI_NOT_FOUND; + } + + FileInfoBuffer = AllocatePool (sizeof(FileInfo) * FileCount); + NoFile = FALSE; + + // + // Get all file info + // + for ( Status = FileHandleFindFirstFile (DirHandle, &FileInfo) + ; !EFI_ERROR (Status) && !NoFile + ; Status = FileHandleFindNextFile (DirHandle, FileInfo, &NoFile) + ){ + if ((FileInfo->Attribute & (EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE)) == 0) { + continue; + } + FileInfoBuffer[Index ++] = AllocateCopyPool (FileInfo->Size, FileInfo); + } + + // + // Sort FileInfoBuffer by alphabet order + // + PerformQuickSort ( + FileInfoBuffer, + FileCount, + sizeof (FileInfo), + (SORT_COMPARE) CompareFileNameInAlphabet + ); + + Print (L"The capsules will be performed by following order:\n"); + + for (Index = 0; Index < FileCount; Index ++) { + Print (L" %d.%s\n", Index + 1, FileInfoBuffer[Index]->FileName); + } + + if (!DumpCapsuleInfo) { + return EFI_SUCCESS; + } + + Print(L"The infomation of the capsules:\n"); + + for (Index = 0; Index < FileCount; Index ++) { + FileHandle = NULL; + Status = DirHandle->Open (DirHandle, &FileHandle, FileInfoBuffer[Index]->FileName, EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + break; + } + + Status = FileHandleGetSize (FileHandle, (UINT64 *) &FileSize); + if (EFI_ERROR (Status)) { + Print (L"Cannot read file %s. Status = %r\n", FileInfoBuffer[Index]->FileName, Status); + FileHandleClose (FileHandle); + return Status; + } + + FileBuffer = AllocatePool (FileSize); + if (FileBuffer == NULL) { + return RETURN_OUT_OF_RESOURCES; + } + + Status = FileHandleRead (FileHandle, &FileSize, FileBuffer); + if (EFI_ERROR (Status)) { + Print (L"Cannot read file %s. Status = %r\n", FileInfoBuffer[Index]->FileName, Status); + FreePool (FileBuffer); + FileHandleClose (FileHandle); + return Status; + } + + Print (L"**************************\n"); + Print (L" %d.%s:\n", Index + 1, FileInfoBuffer[Index]->FileName); + Print (L"**************************\n"); + DumpCapsuleFromBuffer ((EFI_CAPSULE_HEADER *) FileBuffer); + FileHandleClose (FileHandle); + FreePool (FileBuffer); + } + + return EFI_SUCCESS; +} + +/** + Dump capsule inforomation form Gather list. + + @param[in] BlockDescriptors The block descriptors for the capsule images + @param[in] DumpCapsuleInfo The flag to indicate whether to dump the capsule inforomation. + +**/ +VOID +DumpBlockDescriptors ( + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors, + IN BOOLEAN DumpCapsuleInfo + ) +{ + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr; + + TempBlockPtr = BlockDescriptors; + + while (TRUE) { + if (TempBlockPtr->Length != 0) { + if (DumpCapsuleInfo) { + Print(L"******************************************************\n"); + } + Print(L"Capsule data starts at 0x%08x with size 0x%08x\n", TempBlockPtr->Union.DataBlock, TempBlockPtr->Length); + if (DumpCapsuleInfo) { + Print(L"******************************************************\n"); + DumpCapsuleFromBuffer ((EFI_CAPSULE_HEADER *) (UINTN) TempBlockPtr->Union.DataBlock); + } + TempBlockPtr += 1; + } else { + if (TempBlockPtr->Union.ContinuationPointer == (UINTN)NULL) { + break; + } else { + TempBlockPtr = (EFI_CAPSULE_BLOCK_DESCRIPTOR *) (UINTN) TempBlockPtr->Union.ContinuationPointer; + } + } + } +} + +/** + Dump Provisioned Capsule. + + @param[in] DumpCapsuleInfo The flag to indicate whether to dump the capsule inforomation. + +**/ +VOID +DumpProvisionedCapsule ( + IN BOOLEAN DumpCapsuleInfo + ) +{ + EFI_STATUS Status; + CHAR16 CapsuleVarName[30]; + CHAR16 *TempVarName; + UINTN Index; + EFI_PHYSICAL_ADDRESS *CapsuleDataPtr64; + UINT16 *BootNext; + CHAR16 BootOptionName[20]; + EFI_BOOT_MANAGER_LOAD_OPTION BootNextOptionEntry; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; + EFI_SHELL_PROTOCOL *ShellProtocol; + + ShellProtocol = GetShellProtocol (); + + Index = 0; + + // + // Dump capsule provisioned on Memory + // + Print (L"#########################\n"); + Print (L"### Capsule on Memory ###\n"); + Print (L"#########################\n"); + StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CHAR16), EFI_CAPSULE_VARIABLE_NAME); + TempVarName = CapsuleVarName + StrLen (CapsuleVarName); + while (TRUE) { + if (Index > 0) { + UnicodeValueToStringS ( + TempVarName, + sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName), + 0, + Index, + 0 + ); + } + + Status = GetVariable2 ( + CapsuleVarName, + &gEfiCapsuleVendorGuid, + (VOID **) &CapsuleDataPtr64, + NULL + ); + if (EFI_ERROR (Status)) { + if (Index == 0) { + Print (L"No data.\n"); + } + break; + } else { + Index ++; + Print (L"Capsule Description at 0x%08x\n", *CapsuleDataPtr64); + DumpBlockDescriptors ((EFI_CAPSULE_BLOCK_DESCRIPTOR*) (UINTN) *CapsuleDataPtr64, DumpCapsuleInfo); + } + } + + // + // Dump capsule provisioned on Disk + // + Print (L"#########################\n"); + Print (L"### Capsule on Disk #####\n"); + Print (L"#########################\n"); + Status = GetVariable2 ( + L"BootNext", + &gEfiGlobalVariableGuid, + (VOID **) &BootNext, + NULL + ); + if (!EFI_ERROR (Status)) { + UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", *BootNext); + Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOptionEntry); + if (!EFI_ERROR (Status)) { + // + // Display description and device path + // + GetEfiSysPartitionFromBootOptionFilePath (BootNextOptionEntry.FilePath, &DevicePath, &Fs); + if(!EFI_ERROR (Status)) { + Print (L"Capsules are provisioned on BootOption: %s\n", BootNextOptionEntry.Description); + Print (L" %s %s\n", ShellProtocol->GetMapFromDevicePath (&DevicePath), ConvertDevicePathToText(DevicePath, TRUE, TRUE)); + DumpCapsuleFromDisk (Fs, DumpCapsuleInfo); + } + } + } +} + /** Dump FMP information. diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c b/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c new file mode 100644 index 0000000000..6d029f865b --- /dev/null +++ b/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c @@ -0,0 +1,802 @@ +/** @file + Process Capsule On Disk. + + Copyright (c) 2018, Intel Corporation. All rights reserved.
+ This program and the accompanying materials + are licensed and made available under the terms and conditions of the BSD License + which accompanies this distribution. The full text of the license may be found at + http://opensource.org/licenses/bsd-license.php + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, + WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +EFI_GUID mCapsuleOnDiskBootOptionGuid = { 0x4CC29BB7, 0x2413, 0x40A2, { 0xB0, 0x6D, 0x25, 0x3E, 0x37, 0x10, 0xF5, 0x32 } }; + +/** + Get shell protocol. + + @return Pointer to shell protocol. + +**/ +EFI_SHELL_PROTOCOL * +GetShellProtocol ( + VOID + ); + +/** + Get file name from file path + + @return Pointer to file name. + +**/ +CHAR16 * +GetFileNameFromPath ( + CHAR16 *FilePath + ) +{ + EFI_STATUS Status; + EFI_SHELL_PROTOCOL *ShellProtocol; + SHELL_FILE_HANDLE Handle; + EFI_FILE_INFO *FileInfo; + + ShellProtocol = GetShellProtocol (); + if (ShellProtocol == NULL) { + return NULL; + } + + // + // Open file by FileName. + // + Status = ShellProtocol->OpenFileByName ( + FilePath, + &Handle, + EFI_FILE_MODE_READ + ); + if (EFI_ERROR (Status)) { + return NULL; + } + + // + // Get file name from EFI_FILE_INFO. + // + FileInfo = ShellProtocol->GetFileInfo (Handle); + ShellProtocol->CloseFile (Handle); + if (FileInfo == NULL) { + return NULL; + } + + return FileInfo->FileName; +} + +/** + Check if the device path is EFI system parition. + + @retval TRUE DevicePath is a device path for ESP. + @retval FALSE DevicePath is not a device path for ESP. + +**/ +BOOLEAN +IsEfiSysPartitionDevicePath ( + EFI_DEVICE_PATH_PROTOCOL *DevicePath + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + HARDDRIVE_DEVICE_PATH *Hd; + EFI_HANDLE Handle; + + // + // 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); + return EFI_ERROR (Status) ? FALSE : TRUE; + } else { + return FALSE; + } +} + +/** + Dump all EFI System Parition. + +**/ +VOID +DumpAllEfiSysPartition ( + VOID + ) +{ + EFI_HANDLE *SimpleFileSystemHandles; + UINTN NumberSimpleFileSystemHandles; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN NumberEfiSystemPartitions; + EFI_SHELL_PROTOCOL *ShellProtocol; + + ShellProtocol = GetShellProtocol (); + NumberEfiSystemPartitions = 0; + + Print (L"EFI System Partition list:\n"); + + gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &NumberSimpleFileSystemHandles, + &SimpleFileSystemHandles + ); + + for (Index = 0; Index < NumberSimpleFileSystemHandles; Index ++) { + DevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); + if (IsEfiSysPartitionDevicePath (DevicePath)) { + NumberEfiSystemPartitions ++; + Print(L" %s\n %s\n", ShellProtocol->GetMapFromDevicePath (&DevicePath), ConvertDevicePathToText (DevicePath, TRUE, TRUE)); + } + } + + if (NumberEfiSystemPartitions == 0) { + Print(L" No ESP found.\n"); + } +} + +/** + Check if capsule is provisioned. + + @retval TRUE Capsule is provisioned previously. + @retval FALSE No capsule is provisioned. + +**/ +BOOLEAN +IsCapsuleProvisioned ( + VOID + ) +{ + EFI_STATUS Status; + UINT64 OsIndication; + UINTN DataSize; + + OsIndication = 0; + DataSize = sizeof(UINT64); + Status = gRT->GetVariable ( + L"OsIndications", + &gEfiGlobalVariableGuid, + NULL, + &DataSize, + &OsIndication + ); + if (!EFI_ERROR (Status) && + (OsIndication & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) { + return TRUE; + } + + return FALSE; +} + +/** + Get one active Efi System Partition + + @param[out] FsDevicePath The device path of Fs + @param[out] Fs The file system within EfiSysPartition + + @retval EFI_SUCCESS Get file system successfully + @retval EFI_NOT_FOUND No valid file system found + +**/ +EFI_STATUS +GetEfiSysPartition ( + OUT EFI_DEVICE_PATH_PROTOCOL **FsDevicePath, + OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs + ) +{ + EFI_HANDLE *SimpleFileSystemHandles; + UINTN NumberSimpleFileSystemHandles; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_STATUS Status; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiSimpleFileSystemProtocolGuid, + NULL, + &NumberSimpleFileSystemHandles, + &SimpleFileSystemHandles + ); + + if (EFI_ERROR (Status)) { + return EFI_NOT_FOUND; + } + + for (Index = 0; Index < NumberSimpleFileSystemHandles; Index ++) { + DevicePath = DevicePathFromHandle (SimpleFileSystemHandles[Index]); + if (IsEfiSysPartitionDevicePath (DevicePath)) { + Status = gBS->HandleProtocol (SimpleFileSystemHandles[Index], &gEfiSimpleFileSystemProtocolGuid, Fs); + if (!EFI_ERROR (Status)) { + *FsDevicePath = DevicePath; + return EFI_SUCCESS; + } + } + } + + return EFI_NOT_FOUND; +} + +/** + Check if Active Efi System Partition within GPT is in the device path + + @param[in] DevicePath The device path + @param[out] FullPath The device path of Fs + @param[out] Fs The file system within EfiSysPartition + + @retval EFI_SUCCESS Get file system successfully + @retval EFI_NOT_FOUND No valid file system found + @retval others Get file system failed + +**/ +EFI_STATUS +GetEfiSysPartitionFromDevPath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FsDevicePath, + OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *TempDevicePath; + HARDDRIVE_DEVICE_PATH *Hd; + EFI_HANDLE Handle; + + // + // 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, Fs); + if (!EFI_ERROR (Status)) { + *FsDevicePath = DevicePathFromHandle (Handle); + return EFI_SUCCESS; + } + } + } + + return EFI_NOT_FOUND; +} + +/** + Get SimpleFileSystem from boot option file path + + @param[in] DevicePath The file path of boot option + @param[out] FullPath The full device path of boot device + @param[out] Fs The file system within EfiSysPartition + + @retval EFI_SUCCESS Get file system successfully + @retval EFI_NOT_FOUND No valid file system found + @retval others Get file system failed + +**/ +EFI_STATUS +EFIAPI +GetEfiSysPartitionFromBootOptionFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs + ) +{ + EFI_STATUS Status; + EFI_DEVICE_PATH_PROTOCOL *CurFullPath; + EFI_DEVICE_PATH_PROTOCOL *PreFullPath; + EFI_DEVICE_PATH_PROTOCOL *FsFullPath; + + CurFullPath = NULL; + FsFullPath = NULL; + // + // Try every full device Path generated from bootoption + // + do { + PreFullPath = CurFullPath; + CurFullPath = EfiBootManagerGetNextFullDevicePath (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 ( + CHAR16 *DevicePathStr; + + DevicePathStr = ConvertDevicePathToText (CurFullPath, TRUE, TRUE); + if (DevicePathStr != NULL){ + DEBUG ((DEBUG_INFO, "Full device path %s\n", DevicePathStr)); + FreePool (DevicePathStr); + } + ); + + Status = GetEfiSysPartitionFromDevPath (CurFullPath, &FsFullPath, Fs); + } while (EFI_ERROR (Status)); + + if (*Fs != NULL) { + *FullPath = FsFullPath; + return EFI_SUCCESS; + } else { + return EFI_NOT_FOUND; + } +} + +/** + Get a valid SimpleFileSystem within EFI system partition + + @param[In] Map The FS mapping capsule write to + @param[out] BootNext The value of BootNext Variable + @param[out] Handle The file system handle + @param[out] UpdateBootNext The flag to indicate whether update BootNext Variable + + @retval EFI_SUCCESS Get FS successfully + @retval EFI_NOT_FOUND No valid FS found + @retval others Get FS failed + +**/ +EFI_STATUS +EFIAPI +GetUpdateFileSystem ( + IN CHAR16 *Map, + OUT UINT16 *BootNext, + OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs, + OUT BOOLEAN *UpdateBootNext +) +{ + EFI_STATUS Status; + CHAR16 BootOptionName[20]; + UINTN Index; + CONST EFI_DEVICE_PATH_PROTOCOL *MappedDevicePath; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + EFI_DEVICE_PATH_PROTOCOL *FullPath; + UINT16 *BootNextData; + EFI_BOOT_MANAGER_LOAD_OPTION BootNextOption; + EFI_BOOT_MANAGER_LOAD_OPTION *BootOptionBuffer; + UINTN BootOptionCount; + EFI_SHELL_PROTOCOL *ShellProtocol; + EFI_BOOT_MANAGER_LOAD_OPTION NewOption; + + MappedDevicePath = NULL; + ShellProtocol = GetShellProtocol (); + + // + // 1. If Fs is not assigned and there are capsule provisioned before, + // Get EFI system partition from BootNext. + // + if (IsCapsuleProvisioned () && Map == NULL) { + Status = GetVariable2 ( + L"BootNext", + &gEfiGlobalVariableGuid, + &BootNextData, + NULL + ); + if (!EFI_ERROR (Status)) { + UnicodeSPrint (BootOptionName, sizeof (BootOptionName), L"Boot%04x", *BootNextData); + Status = EfiBootManagerVariableToLoadOption (BootOptionName, &BootNextOption); + if (!EFI_ERROR (Status)) { + DevicePath = BootNextOption.FilePath; + Status = GetEfiSysPartitionFromBootOptionFilePath (DevicePath, &FullPath, Fs); + if (!EFI_ERROR (Status)) { + *UpdateBootNext = FALSE; + Print(L"Get EFI system partition from BootNext : %s\n", BootNextOption.Description); + Print(L"%s %s\n", ShellProtocol->GetMapFromDevicePath (&FullPath), ConvertDevicePathToText (FullPath, TRUE, TRUE)); + return EFI_SUCCESS; + } + } + } + } + + // + // Check if Map is valid. + // + if (Map != NULL) { + MappedDevicePath = ShellProtocol->GetDevicePathFromMap (Map); + if (MappedDevicePath == NULL) { + Print(L"'%s' is not a valid mapping.\n", Map); + return EFI_INVALID_PARAMETER; + } else if (!IsEfiSysPartitionDevicePath (DuplicateDevicePath (MappedDevicePath))) { + Print(L"'%s' is not a EFI System Partition.\n", Map); + return EFI_INVALID_PARAMETER; + } + } + + // + // 2. Get EFI system partition form boot options. + // + BootOptionBuffer = EfiBootManagerGetLoadOptions (&BootOptionCount, LoadOptionTypeBoot); + if (BootOptionCount == 0 && Map == NULL) { + return EFI_NOT_FOUND; + } + + for (Index = 0; Index < BootOptionCount; Index ++) { + // + // Get the boot option from the link list + // + DevicePath = BootOptionBuffer[Index].FilePath; + + // + // Skip inactive or legacy boot options + // + if ((BootOptionBuffer[Index].Attributes & LOAD_OPTION_ACTIVE) == 0 || + DevicePathType (DevicePath) == BBS_DEVICE_PATH) { + continue; + } + + DEBUG_CODE ( + 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")); + } + ); + + Status = GetEfiSysPartitionFromBootOptionFilePath (DevicePath, &FullPath, Fs); + if (!EFI_ERROR (Status)) { + if (Map == NULL) { + *BootNext = (UINT16) BootOptionBuffer[Index].OptionNumber; + *UpdateBootNext = TRUE; + Print (L"Found EFI system partition on Boot%04x: %s\n", *BootNext, BootOptionBuffer[Index].Description); + Print (L"%s %s\n", ShellProtocol->GetMapFromDevicePath (&FullPath), ConvertDevicePathToText (FullPath, TRUE, TRUE)); + return EFI_SUCCESS; + } + + if (StrnCmp (Map, ShellProtocol->GetMapFromDevicePath (&FullPath), StrLen (Map)) == 0) { + *BootNext = (UINT16) BootOptionBuffer[Index].OptionNumber; + *UpdateBootNext = TRUE; + Print (L"Found Boot Option on %s : %s\n", Map, BootOptionBuffer[Index].Description); + return EFI_SUCCESS; + } + } + } + + // + // 3. If no ESP is found on boot option, try to find a ESP and create boot option for it. + // + if (Map != NULL) { + // + // If map is assigned, try to get ESP from mapped Fs. + // + DevicePath = DuplicateDevicePath (MappedDevicePath); + Status = GetEfiSysPartitionFromDevPath (DevicePath, &FullPath, Fs); + if (EFI_ERROR (Status)) { + Print (L"Error: Cannot get EFI system partiion from '%s' - %r\n", Map, Status); + return EFI_NOT_FOUND; + } + Print (L"Warning: Cannot find Boot Option on '%s'!\n", Map); + } else { + Status = GetEfiSysPartition (&DevicePath, Fs); + if (EFI_ERROR (Status)) { + Print (L"Error: Cannot find a EFI system partition!\n"); + return EFI_NOT_FOUND; + } + } + + Print (L"Create Boot option for capsule on disk:\n"); + Status = EfiBootManagerInitializeLoadOption ( + &NewOption, + LoadOptionNumberUnassigned, + LoadOptionTypeBoot, + LOAD_OPTION_ACTIVE, + L"UEFI Capsule On Disk", + DevicePath, + (UINT8 *) &mCapsuleOnDiskBootOptionGuid, + sizeof(EFI_GUID) + ); + if (!EFI_ERROR (Status)) { + Status = EfiBootManagerAddLoadOptionVariable (&NewOption, (UINTN) -1); { + if (!EFI_ERROR (Status)) { + *UpdateBootNext = TRUE; + *BootNext = (UINT16) NewOption.OptionNumber; + Print (L" Boot%04x: %s\n", *BootNext, ConvertDevicePathToText(DevicePath, TRUE, TRUE)); + return EFI_SUCCESS; + } + } + } + + Print (L"ERROR: Cannot create boot option! - %r\n", Status); + + return EFI_NOT_FOUND; +} + +/** + Write files to a given SimpleFileSystem. + + @param[in] Buffer The file buffer array + @param[in] BufferSize The file buffer size array + @param[in] FileName The file file name array + @param[in] BufferNum The file buffer number + @param[in] Fs The SimpleFileSystem handle to be written + + @retval EFI_SUCCESS Write file successfully + @retval EFI_NOT_FOUND SFS protocol not found + @retval others Write file failed + +**/ +EFI_STATUS +WriteUpdateFile ( + IN VOID **Buffer, + IN UINTN *BufferSize, + IN CHAR16 **FileName, + IN UINTN BufferNum, + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs +) +{ + EFI_STATUS Status; + EFI_FILE *Root; + CHAR16 *mDirName = L"\\EFI\\UpdateCapsule"; + CHAR16 *mDirName1 = L"\\EFI"; + EFI_FILE_PROTOCOL *DirHandle = NULL; + EFI_FILE *FileHandle = NULL; + UINT64 FileInfo; + UINTN Index = 0; + VOID *Filebuffer; + UINTN FileSize; + + // + // Open Root from SFS + // + Status = Fs->OpenVolume (Fs, &Root); + if (EFI_ERROR (Status)) { + Print (L"Cannot open volume. Status = %r\n", Status); + return EFI_NOT_FOUND; + } + + // + // Ensure that efi and updatecapsule directories exist + // + Status = Root->Open (Root, &DirHandle, mDirName1, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0); + if (EFI_ERROR (Status)) { + Status = Root->Open (Root, &DirHandle, mDirName1, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY); + if (EFI_ERROR (Status)) { + Print(L"Unable to create %s directory\n", mDirName1); + return EFI_NOT_FOUND; + } + } + Status = Root->Open (Root, &DirHandle, mDirName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0); + if (EFI_ERROR (Status)) { + Status = Root->Open (Root, &DirHandle, mDirName, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE, EFI_FILE_DIRECTORY); + if (EFI_ERROR (Status)) { + Print(L"Unable to create %s directory\n", mDirName); + return EFI_NOT_FOUND; + } + } + + for (Index = 0; Index < BufferNum; Index ++) { + FileHandle = NULL; + + // + // Open UpdateCapsule file + // + Status = DirHandle->Open (DirHandle, &FileHandle, FileName[Index], EFI_FILE_MODE_CREATE | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_READ, 0); + if (EFI_ERROR (Status)) { + Print (L"Unable to create %s file\n", FileName[Index]); + return EFI_NOT_FOUND; + } + + // + // Empty the file contents + // + Status = FileHandleGetSize (FileHandle, &FileInfo); + if (EFI_ERROR (Status)) { + FileHandleClose (FileHandle); + Print (L"Error Reading %s\n", FileName[Index]); + return EFI_DEVICE_ERROR; + } + + // + // If the file size is already 0, then it has been empty. + // + if (FileInfo != 0) { + // + // Set the file size to 0. + // + FileInfo = 0; + Status = FileHandleSetSize (FileHandle, FileInfo); + if (EFI_ERROR (Status)) { + Print (L"Error Deleting %s\n", FileName[Index]); + FileHandleClose (FileHandle); + return Status; + } + } + + // + // Write Filebuffer to file + // + Filebuffer = Buffer[Index]; + FileSize = BufferSize[Index]; + Status = FileHandleWrite (FileHandle, &FileSize, Filebuffer); + if (EFI_ERROR (Status)) { + Print (L"Unable to write Capsule Update to %s, Status = %r\n", FileName[Index], Status); + return EFI_NOT_FOUND; + } + + Print (L"Succeed to write %s\n", FileName[Index]); + FileHandleClose (FileHandle); + } + + return EFI_SUCCESS; +} + +/** + Set capsule status variable. + + @param[in] SetCap Set or clear the capsule flag. + + @retval EFI_SUCCESS Succeed to set SetCap variable. + @retval others Fail to set the variable. + +**/ +EFI_STATUS +SetCapsuleStatusVariable ( + BOOLEAN SetCap + ) +{ + EFI_STATUS Status; + UINT64 OsIndication; + UINTN DataSize; + + OsIndication = 0; + DataSize = sizeof(UINT64); + Status = gRT->GetVariable ( + L"OsIndications", + &gEfiGlobalVariableGuid, + NULL, + &DataSize, + &OsIndication + ); + if (EFI_ERROR (Status)) { + OsIndication = 0; + } + if (SetCap) { + OsIndication |= ((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED); + } + else { + OsIndication &= ~((UINT64)EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED); + } + Status = gRT->SetVariable ( + L"OsIndications", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof(UINT64), + &OsIndication + ); + + return Status; +} + +/** + Process Capsule On Disk. + + @param[in] CapsuleBuffer An array of pointer to capsule images + @param[in] FileSize An array of UINTN to capsule images size + @param[in] FilePath An array of capsule images file path + @param[in] NewFileName An array of new capsule images name + @param[in] CapsuleNum The count of capsule images + + @retval EFI_SUCCESS Capsule on disk secceed. + @retval others Capsule on disk fail. + +**/ +EFI_STATUS +ProcessCapsuleOnDisk ( + IN VOID **CapsuleBuffer, + IN UINTN *CapsuleBufferSize, + IN CHAR16 *Map, + IN CHAR16 **OutputCapsuleNames, + IN UINTN CapsuleNum + ) +{ + EFI_STATUS Status; + UINT16 BootNext; + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *Fs; + BOOLEAN UpdateBootNext; + + // + // Get a valid file system from boot path + // + Fs = NULL; + + Status = GetUpdateFileSystem (Map, &BootNext, &Fs, &UpdateBootNext); + if (EFI_ERROR (Status)) { + Print (L"CapsuleApp: cannot find a valid file system on boot devies. Status = %r\n", Status); + return Status; + } + + // + // Copy capsule image to '\efi\UpdateCapsule\' + // + Status = WriteUpdateFile (CapsuleBuffer, CapsuleBufferSize, OutputCapsuleNames, CapsuleNum, Fs); + if (EFI_ERROR (Status)) { + Print (L"CapsuleApp: capsule image could not be copied for update.\n"); + return Status; + } + + // + // Set variable then reset + // + Status = SetCapsuleStatusVariable (TRUE); + if (EFI_ERROR (Status)) { + Print (L"CapsuleApp: unable to set OSIndication variable.\n"); + return Status; + } + + if (UpdateBootNext) { + Status = gRT->SetVariable ( + L"BootNext", + &gEfiGlobalVariableGuid, + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, + sizeof(UINT16), + &BootNext + ); + if (EFI_ERROR (Status)){ + Print (L"CapsuleApp: unable to set BootNext variable.\n"); + return Status; + } + } + + return EFI_SUCCESS; +} \ No newline at end of file diff --git a/MdeModulePkg/Include/Library/UefiBootManagerLib.h b/MdeModulePkg/Include/Library/UefiBootManagerLib.h index bfc0cb86f8..9302398936 100644 --- a/MdeModulePkg/Include/Library/UefiBootManagerLib.h +++ b/MdeModulePkg/Include/Library/UefiBootManagerLib.h @@ -445,6 +445,25 @@ EfiBootManagerGetBootManagerMenu ( EFI_BOOT_MANAGER_LOAD_OPTION *BootOption ); +/** + Get the next possible full path pointing to the load option. + The routine doesn't guarantee the returned full path points to an existing + file, and it also doesn't guarantee the existing file is a valid load option. + BmGetNextLoadOptionBuffer() guarantees. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +EfiBootManagerGetNextFullDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath + ); /** Get the load option by its device path. diff --git a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c index 6a23477eb8..5d3acb6e7b 100644 --- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c +++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c @@ -2461,3 +2461,26 @@ EfiBootManagerGetBootManagerMenu ( } } +/** + Get the next possible full path pointing to the load option. + The routine doesn't guarantee the returned full path points to an existing + file, and it also doesn't guarantee the existing file is a valid load option. + BmGetNextLoadOptionBuffer() guarantees. + + @param FilePath The device path pointing to a load option. + It could be a short-form device path. + @param FullPath The full path returned by the routine in last call. + Set to NULL in first call. + + @return The next possible full path pointing to the load option. + Caller is responsible to free the memory. +**/ +EFI_DEVICE_PATH_PROTOCOL * +EfiBootManagerGetNextFullDevicePath ( + IN EFI_DEVICE_PATH_PROTOCOL *FilePath, + IN EFI_DEVICE_PATH_PROTOCOL *FullPath + ) +{ + return BmGetNextLoadOptionDevicePath(FilePath, FullPath); +} + -- 2.16.2.windows.1