From: Chen A Chen <chen.a.chen@intel.com>
To: edk2-devel@lists.01.org
Cc: Chen A Chen <chen.a.chen@intel.com>,
Jian J Wang <jian.j.wang@intel.com>, Hao Wu <hao.a.wu@intel.com>,
Zhang Chao B <chao.b.zhang@intel.com>
Subject: [PATCH v2 2/2] MdeModulePkg/CapsuleApp: Enhance CapsuleApp to support Capsule-on-Disk.
Date: Mon, 28 Jan 2019 09:21:15 +0800 [thread overview]
Message-ID: <20190128012115.11124-1-chen.a.chen@intel.com> (raw)
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 <jian.j.wang@intel.com>
Cc: Hao Wu <hao.a.wu@intel.com>
Cc: Zhang Chao B <chao.b.zhang@intel.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Chen A Chen <chen.a.chen@intel.com>
---
MdeModulePkg/Application/CapsuleApp/CapsuleApp.c | 153 +++-
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 | 22 +
6 files changed, 1524 insertions(+), 15 deletions(-)
create mode 100644 MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c
diff --git a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
index 4d907242f3..ca9baa0a6a 100644
--- a/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
+++ b/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
@@ -23,6 +23,7 @@
#include <Library/PrintLib.h>
#include <Library/BmpSupportLib.h>
#include <Protocol/GraphicsOutput.h>
+#include <Guid/GlobalVariable.h>
#include <Guid/CapsuleReport.h>
#include <Guid/SystemResourceTable.h>
#include <Guid/FmpCapsule.h>
@@ -105,6 +106,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 **FilePath,
+ IN CHAR16 *Map,
+ IN UINTN CapsuleNum
+ );
+
/**
Read a file.
@@ -799,19 +838,22 @@ PrintUsage (
)
{
Print(L"CapsuleApp: usage\n");
- Print(L" CapsuleApp <Capsule...> [-NR]\n");
+ Print(L" CapsuleApp <Capsule...> [-NR] [-OD [FSx]]\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 <BMP> -O <Capsule>\n");
Print(L" CapsuleApp -N <Capsule> -O <NestedCapsule>\n");
Print(L" CapsuleApp -D <Capsule>\n");
Print(L" CapsuleApp -P GET <ImageTypeId> <Index> -O <FileName>\n");
Print(L"Parameter:\n");
- Print(L" -NR: No reset will be triggered for the capsule with\n");
- Print(L" CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without\n");
- Print(L" CAPSULE_FLAGS_INITIATE_RESET.\n");
+ Print(L" -NR: No reset will be triggered for the capsule\n");
+ Print(L" with CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without CAPSULE_FLAGS_INITIATE_RESET.\n");
+ Print(L" -OD: Delivery of Capsules via file on Mass Storage device.");
Print(L" -S: Dump capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");
Print(L" which is defined in UEFI specification.\n");
Print(L" -C: Clear capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");
@@ -820,6 +862,8 @@ PrintUsage (
Print(L" ImageTypeId and Index (decimal format) to a file if 'GET'\n");
Print(L" option is used.\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");
@@ -851,7 +895,7 @@ UefiMain (
{
EFI_STATUS Status;
RETURN_STATUS RStatus;
- UINTN FileSize[MAX_CAPSULE_NUM];
+ UINTN CapsuleBufferSize[MAX_CAPSULE_NUM];
VOID *CapsuleBuffer[MAX_CAPSULE_NUM];
EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;
EFI_CAPSULE_HEADER *CapsuleHeaderArray[MAX_CAPSULE_NUM + 1];
@@ -859,9 +903,14 @@ UefiMain (
EFI_RESET_TYPE ResetType;
BOOLEAN NeedReset;
BOOLEAN NoReset;
+ BOOLEAN CapsuleOnDisk;
CHAR16 *CapsuleName;
+ CHAR16 *CapsuleNames[MAX_CAPSULE_NUM];
+ CHAR16 *MapFsStr;
UINTN CapsuleNum;
UINTN Index;
+ UINTN ParaOdIndex;
+ UINTN ParaNrIndex;
EFI_GUID ImageTypeId;
UINTN ImageIndex;
@@ -936,6 +985,20 @@ UefiMain (
return EFI_SUCCESS;
}
+ if (StrCmp(Argv[1], L"-L") == 0) {
+ if (Argc >= 3 && StrCmp(Argv[2], L"INFO") == 0) {
+ DumpProvisionedCapsule(TRUE);
+ } else {
+ DumpProvisionedCapsule(FALSE);
+ }
+ return EFI_SUCCESS;
+ }
+
+ if (StrCmp(Argv[1], L"-F") == 0) {
+ DumpAllEfiSysPartition();
+ return EFI_SUCCESS;
+ }
+
if (Argv[1][0] == L'-') {
Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[1]);
return EFI_UNSUPPORTED;
@@ -943,12 +1006,56 @@ UefiMain (
CapsuleFirstIndex = 1;
NoReset = FALSE;
- if ((Argc > 1) && (StrCmp(Argv[Argc - 1], L"-NR") == 0)) {
- NoReset = TRUE;
- CapsuleLastIndex = Argc - 2;
+ CapsuleOnDisk = FALSE;
+ ParaOdIndex = 0;
+ ParaNrIndex = 0;
+
+ for (Index = 1; Index < Argc; Index ++) {
+ if (StrCmp(Argv[Index], L"-OD") == 0) {
+ ParaOdIndex = Index;
+ CapsuleOnDisk = TRUE;
+ } else if (StrCmp(Argv[Index], L"-NR") == 0) {
+ ParaNrIndex = Index;
+ NoReset = TRUE;
+ }
+ }
+
+ if (ParaOdIndex != 0) {
+ if (ParaOdIndex == Argc - 1) {
+ MapFsStr = NULL;
+ } else if (ParaOdIndex == Argc - 2) {
+ MapFsStr = Argv[Argc-1];
+ } else {
+ Print (L"CapsuleApp: Invalid Position for -OD Options\n");
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+
+ if (ParaNrIndex != 0) {
+ if (ParaNrIndex + 1 == ParaOdIndex) {
+ CapsuleLastIndex = ParaNrIndex - 1;
+ } else {
+ Print (L"CapsuleApp: Invalid Position for -NR Options\n");
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+ } else {
+ CapsuleLastIndex = ParaOdIndex - 1;
+ }
} else {
- CapsuleLastIndex = Argc - 1;
+ if (ParaNrIndex != 0) {
+ if (ParaNrIndex == Argc -1) {
+ CapsuleLastIndex = ParaNrIndex - 1;
+ } else {
+ Print (L"CapsuleApp: Invalid Position for -NR Options\n");
+ Status = EFI_INVALID_PARAMETER;
+ goto Done;
+ }
+ } else {
+ CapsuleLastIndex = Argc - 1;
+ }
}
+
CapsuleNum = CapsuleLastIndex - CapsuleFirstIndex + 1;
if (CapsuleFirstIndex > CapsuleLastIndex) {
@@ -961,26 +1068,27 @@ UefiMain (
}
ZeroMem(&CapsuleBuffer, sizeof(CapsuleBuffer));
- ZeroMem(&FileSize, sizeof(FileSize));
+ ZeroMem(&CapsuleBufferSize, sizeof(CapsuleBufferSize));
BlockDescriptors = NULL;
for (Index = 0; Index < CapsuleNum; Index++) {
CapsuleName = Argv[CapsuleFirstIndex + Index];
- Status = ReadFileToBuffer(CapsuleName, &FileSize[Index], &CapsuleBuffer[Index]);
+ Status = ReadFileToBuffer(CapsuleName, &CapsuleBufferSize[Index], &CapsuleBuffer[Index]);
if (EFI_ERROR(Status)) {
Print(L"CapsuleApp: capsule image (%s) is not found.\n", CapsuleName);
goto Done;
}
- if (!IsValidCapsuleHeader (CapsuleBuffer[Index], FileSize[Index])) {
+ if (!IsValidCapsuleHeader (CapsuleBuffer[Index], CapsuleBufferSize[Index])) {
Print(L"CapsuleApp: Capsule image (%s) is not a valid capsule.\n", CapsuleName);
return EFI_INVALID_PARAMETER;
}
+ CapsuleNames[Index] = CapsuleName;
}
//
// Every capsule use 2 descriptor 1 for data 1 for end
//
- Status = BuildGatherList(CapsuleBuffer, FileSize, CapsuleNum, &BlockDescriptors);
+ Status = BuildGatherList(CapsuleBuffer, CapsuleBufferSize, CapsuleNum, &BlockDescriptors);
if (EFI_ERROR(Status)) {
goto Done;
}
@@ -1007,13 +1115,30 @@ UefiMain (
}
for (Index = 0; Index < CapsuleNum; Index++) {
- if (FileSize[Index] > MaxCapsuleSize) {
+ if (CapsuleBufferSize[Index] > MaxCapsuleSize) {
Print (L"CapsuleApp: capsule is too large to update, %ld is allowed\n", MaxCapsuleSize);
Status = EFI_UNSUPPORTED;
goto Done;
}
}
+ //
+ // Check whether is capsule on disk.
+ //
+ if (CapsuleOnDisk) {
+ Status = ProcessCapsuleOnDisk (CapsuleBuffer, CapsuleBufferSize, CapsuleNames, MapFsStr, CapsuleNum);
+ if (Status != EFI_SUCCESS) {
+ Print (L"CapsuleApp: failed to update capsule - %r\n", Status);
+ goto Done;
+ } else {
+ if (!NoReset) {
+ 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 8a21875286..0334e0caaf 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]
@@ -40,16 +41,20 @@
MdeModulePkg/MdeModulePkg.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
@@ -61,6 +66,9 @@
UefiLib
PrintLib
BmpSupportLib
+ 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 <Library/UefiRuntimeServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
+#include <Library/FileHandleLib.h>
+#include <Library/SortLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Library/DevicePathLib.h>
#include <Protocol/FirmwareManagement.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/Shell.h>
#include <Guid/ImageAuthentication.h>
#include <Guid/CapsuleReport.h>
#include <Guid/SystemResourceTable.h>
#include <Guid/FmpCapsule.h>
+#include <Guid/CapsuleVendor.h>
#include <IndustryStandard/WindowsUxCapsule.h>
+//
+// (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..ad9ad9ba87
--- /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.<BR>
+ This program and the accompanying materials
+ are licensed and made available under the terms and conditions of the BSD License
+ which accompanies this distribution. The full text of the license may be found at
+ http://opensource.org/licenses/bsd-license.php
+
+ THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+#include <Uefi.h>
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiLib.h>
+#include <Library/PrintLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/FileHandleLib.h>
+#include <Library/UefiBootManagerLib.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Protocol/Shell.h>
+#include <Guid/FileInfo.h>
+#include <Guid/GlobalVariable.h>
+#include <Guid/Gpt.h>
+
+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 **FilePath,
+ IN CHAR16 *Map,
+ 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, FilePath, 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..3b9195ba50 100644
--- a/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
+++ b/MdeModulePkg/Library/UefiBootManagerLib/BmBoot.c
@@ -2461,3 +2461,25 @@ 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
next reply other threads:[~2019-01-28 1:21 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2019-01-28 1:21 Chen A Chen [this message]
2019-01-28 2:57 ` [PATCH v2 2/2] MdeModulePkg/CapsuleApp: Enhance CapsuleApp to support Capsule-on-Disk Wang, Jian J
2019-01-29 3:24 ` Wang, Jian J
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20190128012115.11124-1-chen.a.chen@intel.com \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox