From af1a266670d040d2f4083ff309d732d648afba2a Mon Sep 17 00:00:00 2001 From: Angelos Mouzakitis Date: Tue, 10 Oct 2023 14:33:42 +0000 Subject: Add submodule dependency files Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec --- .../Application/CapsuleApp/AppSupport.c | 232 ++++ .../Application/CapsuleApp/CapsuleApp.c | 1015 ++++++++++++++ .../Application/CapsuleApp/CapsuleApp.h | 240 ++++ .../Application/CapsuleApp/CapsuleApp.inf | 69 + .../Application/CapsuleApp/CapsuleApp.uni | 17 + .../Application/CapsuleApp/CapsuleAppExtra.uni | 14 + .../Application/CapsuleApp/CapsuleDump.c | 1444 ++++++++++++++++++++ .../Application/CapsuleApp/CapsuleOnDisk.c | 842 ++++++++++++ 8 files changed, 3873 insertions(+) create mode 100644 roms/edk2/MdeModulePkg/Application/CapsuleApp/AppSupport.c create mode 100644 roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c create mode 100644 roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.h create mode 100644 roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf create mode 100644 roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.uni create mode 100644 roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleAppExtra.uni create mode 100644 roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c create mode 100644 roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c (limited to 'roms/edk2/MdeModulePkg/Application/CapsuleApp') diff --git a/roms/edk2/MdeModulePkg/Application/CapsuleApp/AppSupport.c b/roms/edk2/MdeModulePkg/Application/CapsuleApp/AppSupport.c new file mode 100644 index 000000000..8fe70dc3b --- /dev/null +++ b/roms/edk2/MdeModulePkg/Application/CapsuleApp/AppSupport.c @@ -0,0 +1,232 @@ +/** @file + A shell application that triggers capsule update process. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleApp.h" + +UINTN Argc; +CHAR16 **Argv; +EFI_SHELL_PROTOCOL *mShellProtocol = NULL; + +/** + + This function parse application ARG. + + @return Status +**/ +EFI_STATUS +GetArg ( + VOID + ) +{ + EFI_STATUS Status; + EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters; + + Status = gBS->HandleProtocol ( + gImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID**)&ShellParameters + ); + if (EFI_ERROR(Status)) { + return Status; + } + + Argc = ShellParameters->Argc; + Argv = ShellParameters->Argv; + return EFI_SUCCESS; +} + +/** + Get shell protocol. + + @return Pointer to shell protocol. +**/ +EFI_SHELL_PROTOCOL * +GetShellProtocol ( + VOID + ) +{ + EFI_STATUS Status; + + if (mShellProtocol == NULL) { + Status = gBS->LocateProtocol ( + &gEfiShellProtocolGuid, + NULL, + (VOID **) &mShellProtocol + ); + if (EFI_ERROR (Status)) { + mShellProtocol = NULL; + } + } + + return mShellProtocol; +} + +/** + Read a file. + + @param[in] FileName The file to be read. + @param[out] BufferSize The file buffer size + @param[out] Buffer The file buffer + + @retval EFI_SUCCESS Read file successfully + @retval EFI_NOT_FOUND Shell protocol or file not found + @retval others Read file failed +**/ +EFI_STATUS +ReadFileToBuffer ( + IN CHAR16 *FileName, + OUT UINTN *BufferSize, + OUT VOID **Buffer + ) +{ + EFI_STATUS Status; + EFI_SHELL_PROTOCOL *ShellProtocol; + SHELL_FILE_HANDLE Handle; + UINT64 FileSize; + UINTN TempBufferSize; + VOID *TempBuffer; + + ShellProtocol = GetShellProtocol(); + if (ShellProtocol == NULL) { + return EFI_NOT_FOUND; + } + + // + // Open file by FileName. + // + Status = ShellProtocol->OpenFileByName ( + FileName, + &Handle, + EFI_FILE_MODE_READ + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Get the file size. + // + Status = ShellProtocol->GetFileSize (Handle, &FileSize); + if (EFI_ERROR (Status)) { + ShellProtocol->CloseFile (Handle); + return Status; + } + + TempBufferSize = (UINTN) FileSize; + TempBuffer = AllocateZeroPool (TempBufferSize); + if (TempBuffer == NULL) { + ShellProtocol->CloseFile (Handle); + return EFI_OUT_OF_RESOURCES; + } + + // + // Read the file data to the buffer + // + Status = ShellProtocol->ReadFile ( + Handle, + &TempBufferSize, + TempBuffer + ); + if (EFI_ERROR (Status)) { + ShellProtocol->CloseFile (Handle); + return Status; + } + + ShellProtocol->CloseFile (Handle); + + *BufferSize = TempBufferSize; + *Buffer = TempBuffer; + return EFI_SUCCESS; +} + +/** + Write a file. + + @param[in] FileName The file to be written. + @param[in] BufferSize The file buffer size + @param[in] Buffer The file buffer + + @retval EFI_SUCCESS Write file successfully + @retval EFI_NOT_FOUND Shell protocol not found + @retval others Write file failed +**/ +EFI_STATUS +WriteFileFromBuffer ( + IN CHAR16 *FileName, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + EFI_SHELL_PROTOCOL *ShellProtocol; + SHELL_FILE_HANDLE Handle; + EFI_FILE_INFO *FileInfo; + UINTN TempBufferSize; + + ShellProtocol = GetShellProtocol(); + if (ShellProtocol == NULL) { + return EFI_NOT_FOUND; + } + + // + // Open file by FileName. + // + Status = ShellProtocol->OpenFileByName ( + FileName, + &Handle, + EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Empty the file contents. + // + FileInfo = ShellProtocol->GetFileInfo (Handle); + if (FileInfo == NULL) { + ShellProtocol->CloseFile (Handle); + return EFI_DEVICE_ERROR; + } + + // + // If the file size is already 0, then it has been empty. + // + if (FileInfo->FileSize != 0) { + // + // Set the file size to 0. + // + FileInfo->FileSize = 0; + Status = ShellProtocol->SetFileInfo (Handle, FileInfo); + if (EFI_ERROR (Status)) { + FreePool (FileInfo); + ShellProtocol->CloseFile (Handle); + return Status; + } + } + FreePool (FileInfo); + + // + // Write the file data from the buffer + // + TempBufferSize = BufferSize; + Status = ShellProtocol->WriteFile ( + Handle, + &TempBufferSize, + Buffer + ); + if (EFI_ERROR (Status)) { + ShellProtocol->CloseFile (Handle); + return Status; + } + + ShellProtocol->CloseFile (Handle); + + return EFI_SUCCESS; +} + diff --git a/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c new file mode 100644 index 000000000..403471477 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c @@ -0,0 +1,1015 @@ +/** @file + A shell application that triggers capsule update process. + + Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleApp.h" + +// +// Define how many block descriptors we want to test with. +// +UINTN NumberOfDescriptors = 1; +UINTN CapsuleFirstIndex; +UINTN CapsuleLastIndex; + +/** + Create UX capsule. + + @retval EFI_SUCCESS The capsule header is appended. + @retval EFI_UNSUPPORTED Input parameter is not valid. + @retval EFI_OUT_OF_RESOURCES No enough resource to create UX capsule. +**/ +EFI_STATUS +CreateBmpFmp ( + VOID + ) +{ + CHAR16 *OutputCapsuleName; + VOID *BmpBuffer; + UINTN FileSize; + CHAR16 *BmpName; + UINT8 *FullCapsuleBuffer; + UINTN FullCapsuleBufferSize; + EFI_DISPLAY_CAPSULE *DisplayCapsule; + EFI_STATUS Status; + EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop; + EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info; + EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GopBlt; + UINTN GopBltSize; + UINTN Height; + UINTN Width; + + Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&Gop); + if (EFI_ERROR(Status)) { + Print(L"CapsuleApp: NO GOP is found.\n"); + return EFI_UNSUPPORTED; + } + Info = Gop->Mode->Info; + Print(L"Current GOP: Mode - %d, ", Gop->Mode->Mode); + Print(L"HorizontalResolution - %d, ", Info->HorizontalResolution); + Print(L"VerticalResolution - %d\n", Info->VerticalResolution); + // HorizontalResolution >= BMP_IMAGE_HEADER.PixelWidth + // VerticalResolution >= BMP_IMAGE_HEADER.PixelHeight + + if (Argc != 5) { + Print(L"CapsuleApp: Incorrect parameter count.\n"); + return EFI_UNSUPPORTED; + } + + if (StrCmp(Argv[3], L"-O") != 0) { + Print(L"CapsuleApp: NO output capsule name.\n"); + return EFI_UNSUPPORTED; + } + OutputCapsuleName = Argv[4]; + + BmpBuffer = NULL; + FileSize = 0; + FullCapsuleBuffer = NULL; + + BmpName = Argv[2]; + Status = ReadFileToBuffer(BmpName, &FileSize, &BmpBuffer); + if (EFI_ERROR(Status)) { + Print(L"CapsuleApp: BMP image (%s) is not found.\n", BmpName); + goto Done; + } + + GopBlt = NULL; + Status = TranslateBmpToGopBlt ( + BmpBuffer, + FileSize, + &GopBlt, + &GopBltSize, + &Height, + &Width + ); + if (EFI_ERROR(Status)) { + Print(L"CapsuleApp: BMP image (%s) is not valid.\n", BmpName); + goto Done; + } + if (GopBlt != NULL) { + FreePool (GopBlt); + } + Print(L"BMP image (%s), Width - %d, Height - %d\n", BmpName, Width, Height); + + if (Height > Info->VerticalResolution) { + Status = EFI_INVALID_PARAMETER; + Print(L"CapsuleApp: BMP image (%s) height is larger than current resolution.\n", BmpName); + goto Done; + } + if (Width > Info->HorizontalResolution) { + Status = EFI_INVALID_PARAMETER; + Print(L"CapsuleApp: BMP image (%s) width is larger than current resolution.\n", BmpName); + goto Done; + } + + FullCapsuleBufferSize = sizeof(EFI_DISPLAY_CAPSULE) + FileSize; + FullCapsuleBuffer = AllocatePool(FullCapsuleBufferSize); + if (FullCapsuleBuffer == NULL) { + Print(L"CapsuleApp: Capsule Buffer size (0x%x) too big.\n", FullCapsuleBufferSize); + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + DisplayCapsule = (EFI_DISPLAY_CAPSULE *)FullCapsuleBuffer; + CopyGuid(&DisplayCapsule->CapsuleHeader.CapsuleGuid, &gWindowsUxCapsuleGuid); + DisplayCapsule->CapsuleHeader.HeaderSize = sizeof(DisplayCapsule->CapsuleHeader); + DisplayCapsule->CapsuleHeader.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; + DisplayCapsule->CapsuleHeader.CapsuleImageSize = (UINT32)FullCapsuleBufferSize; + + DisplayCapsule->ImagePayload.Version = 1; + DisplayCapsule->ImagePayload.Checksum = 0; + DisplayCapsule->ImagePayload.ImageType = 0; // BMP + DisplayCapsule->ImagePayload.Reserved = 0; + DisplayCapsule->ImagePayload.Mode = Gop->Mode->Mode; + + // + // Center the bitmap horizontally + // + DisplayCapsule->ImagePayload.OffsetX = (UINT32)((Info->HorizontalResolution - Width) / 2); + + // + // Put bitmap 3/4 down the display. If bitmap is too tall, then align bottom + // of bitmap at bottom of display. + // + DisplayCapsule->ImagePayload.OffsetY = + MIN ( + (UINT32)(Info->VerticalResolution - Height), + (UINT32)(((3 * Info->VerticalResolution) - (2 * Height)) / 4) + ); + + Print(L"BMP image (%s), OffsetX - %d, OffsetY - %d\n", + BmpName, + DisplayCapsule->ImagePayload.OffsetX, + DisplayCapsule->ImagePayload.OffsetY + ); + + CopyMem((DisplayCapsule + 1), BmpBuffer, FileSize); + + DisplayCapsule->ImagePayload.Checksum = CalculateCheckSum8(FullCapsuleBuffer, FullCapsuleBufferSize); + + Status = WriteFileFromBuffer(OutputCapsuleName, FullCapsuleBufferSize, FullCapsuleBuffer); + Print(L"CapsuleApp: Write %s %r\n", OutputCapsuleName, Status); + +Done: + if (BmpBuffer != NULL) { + FreePool(BmpBuffer); + } + + if (FullCapsuleBuffer != NULL) { + FreePool(FullCapsuleBuffer); + } + + return Status; +} + +/** + Get ImageTypeId in the FMP capsule header. + + @param[in] CapsuleHeader The FMP capsule image header. + + @return ImageTypeId +**/ +EFI_GUID * +GetCapsuleImageTypeId ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader; + UINT64 *ItemOffsetList; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader; + + FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize); + ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1); + if (FmpCapsuleHeader->PayloadItemCount == 0) { + return NULL; + } + ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[FmpCapsuleHeader->EmbeddedDriverCount]); + return &ImageHeader->UpdateImageTypeId; +} + +/** + Get ESRT FwType according to ImageTypeId + + @param[in] ImageTypeId ImageTypeId of an FMP capsule. + + @return ESRT FwType +**/ +UINT32 +GetEsrtFwType ( + IN EFI_GUID *ImageTypeId + ) +{ + EFI_STATUS Status; + EFI_SYSTEM_RESOURCE_TABLE *Esrt; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry; + UINTN Index; + + // + // Check ESRT + // + Status = EfiGetSystemConfigurationTable(&gEfiSystemResourceTableGuid, (VOID **)&Esrt); + if (!EFI_ERROR(Status)) { + ASSERT(Esrt != NULL); + EsrtEntry = (VOID *)(Esrt + 1); + for (Index = 0; Index < Esrt->FwResourceCount; Index++, EsrtEntry++) { + if (CompareGuid(&EsrtEntry->FwClass, ImageTypeId)) { + return EsrtEntry->FwType; + } + } + } + + return ESRT_FW_TYPE_UNKNOWN; +} + +/** + Validate if it is valid capsule header + + This function assumes the caller provided correct CapsuleHeader pointer + and CapsuleSize. + + This function validates the fields in EFI_CAPSULE_HEADER. + + @param[in] CapsuleHeader Points to a capsule header. + @param[in] CapsuleSize Size of the whole capsule image. + +**/ +BOOLEAN +IsValidCapsuleHeader ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN UINT64 CapsuleSize + ) +{ + if (CapsuleSize < sizeof (EFI_CAPSULE_HEADER)) { + return FALSE; + } + if (CapsuleHeader->CapsuleImageSize != CapsuleSize) { + return FALSE; + } + if (CapsuleHeader->HeaderSize > CapsuleHeader->CapsuleImageSize) { + return FALSE; + } + if (CapsuleHeader->HeaderSize < sizeof (EFI_CAPSULE_HEADER)) { + return FALSE; + } + + return TRUE; +} + +/** + Return if this CapsuleGuid is a FMP capsule GUID or not. + + @param[in] CapsuleGuid A pointer to EFI_GUID + + @retval TRUE It is a FMP capsule GUID. + @retval FALSE It is not a FMP capsule GUID. +**/ +BOOLEAN +IsFmpCapsuleGuid ( + IN EFI_GUID *CapsuleGuid + ) +{ + if (CompareGuid(&gEfiFmpCapsuleGuid, CapsuleGuid)) { + return TRUE; + } + + return FALSE; +} + +/** + Append a capsule header on top of current image. + This function follows Windows UEFI Firmware Update Platform document. + + @retval EFI_SUCCESS The capsule header is appended. + @retval EFI_UNSUPPORTED Input parameter is not valid. + @retval EFI_OUT_OF_RESOURCES No enough resource to append capsule header. +**/ +EFI_STATUS +CreateNestedFmp ( + VOID + ) +{ + CHAR16 *OutputCapsuleName; + VOID *CapsuleBuffer; + UINTN FileSize; + CHAR16 *CapsuleName; + UINT8 *FullCapsuleBuffer; + UINTN FullCapsuleBufferSize; + EFI_CAPSULE_HEADER *NestedCapsuleHeader; + EFI_GUID *ImageTypeId; + UINT32 FwType; + EFI_STATUS Status; + + if (Argc != 5) { + Print(L"CapsuleApp: Incorrect parameter count.\n"); + return EFI_UNSUPPORTED; + } + + if (StrCmp(Argv[3], L"-O") != 0) { + Print(L"CapsuleApp: NO output capsule name.\n"); + return EFI_UNSUPPORTED; + } + OutputCapsuleName = Argv[4]; + + CapsuleBuffer = NULL; + FileSize = 0; + FullCapsuleBuffer = NULL; + + CapsuleName = Argv[2]; + Status = ReadFileToBuffer(CapsuleName, &FileSize, &CapsuleBuffer); + if (EFI_ERROR(Status)) { + Print(L"CapsuleApp: Capsule image (%s) is not found.\n", CapsuleName); + goto Done; + } + if (!IsValidCapsuleHeader (CapsuleBuffer, FileSize)) { + Print(L"CapsuleApp: Capsule image (%s) is not a valid capsule.\n", CapsuleName); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + if (!IsFmpCapsuleGuid (&((EFI_CAPSULE_HEADER *) CapsuleBuffer)->CapsuleGuid)) { + Print(L"CapsuleApp: Capsule image (%s) is not a FMP capsule.\n", CapsuleName); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + ImageTypeId = GetCapsuleImageTypeId(CapsuleBuffer); + if (ImageTypeId == NULL) { + Print(L"CapsuleApp: Capsule ImageTypeId is not found.\n"); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + FwType = GetEsrtFwType(ImageTypeId); + if ((FwType != ESRT_FW_TYPE_SYSTEMFIRMWARE) && (FwType != ESRT_FW_TYPE_DEVICEFIRMWARE)) { + Print(L"CapsuleApp: Capsule FwType is invalid.\n"); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + FullCapsuleBufferSize = NESTED_CAPSULE_HEADER_SIZE + FileSize; + FullCapsuleBuffer = AllocatePool(FullCapsuleBufferSize); + if (FullCapsuleBuffer == NULL) { + Print(L"CapsuleApp: Capsule Buffer size (0x%x) too big.\n", FullCapsuleBufferSize); + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + NestedCapsuleHeader = (EFI_CAPSULE_HEADER *)FullCapsuleBuffer; + ZeroMem(NestedCapsuleHeader, NESTED_CAPSULE_HEADER_SIZE); + CopyGuid(&NestedCapsuleHeader->CapsuleGuid, ImageTypeId); + NestedCapsuleHeader->HeaderSize = NESTED_CAPSULE_HEADER_SIZE; + NestedCapsuleHeader->Flags = (FwType == ESRT_FW_TYPE_SYSTEMFIRMWARE) ? SYSTEM_FIRMWARE_FLAG : DEVICE_FIRMWARE_FLAG; + NestedCapsuleHeader->CapsuleImageSize = (UINT32)FullCapsuleBufferSize; + + CopyMem((UINT8 *)NestedCapsuleHeader + NestedCapsuleHeader->HeaderSize, CapsuleBuffer, FileSize); + + Status = WriteFileFromBuffer(OutputCapsuleName, FullCapsuleBufferSize, FullCapsuleBuffer); + Print(L"CapsuleApp: Write %s %r\n", OutputCapsuleName, Status); + +Done: + if (CapsuleBuffer != NULL) { + FreePool(CapsuleBuffer); + } + + if (FullCapsuleBuffer != NULL) { + FreePool(FullCapsuleBuffer); + } + + return Status; +} + + +/** + Clear capsule status variable. + + @retval EFI_SUCCESS The capsule status variable is cleared. +**/ +EFI_STATUS +ClearCapsuleStatusVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 Index; + CHAR16 CapsuleVarName[20]; + CHAR16 *TempVarName; + BOOLEAN Found; + + StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), L"Capsule"); + TempVarName = CapsuleVarName + StrLen (CapsuleVarName); + Index = 0; + + Found = FALSE; + while (TRUE) { + UnicodeSPrint (TempVarName, 5 * sizeof(CHAR16), L"%04x", Index); + + Status = gRT->SetVariable ( + CapsuleVarName, + &gEfiCapsuleReportGuid, + EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS, + 0, + (VOID *)NULL + ); + if (Status == EFI_NOT_FOUND) { + // + // There is no more capsule variables, quit + // + break; + } + Found = TRUE; + + Print (L"Clear %s %r\n", CapsuleVarName, Status); + + Index++; + if (Index > 0xFFFF) { + break; + } + } + + if (!Found) { + Print (L"No any Capsule#### variable found\n"); + } + + return EFI_SUCCESS; +} + +/** + Build Gather list for a list of capsule images. + + @param[in] CapsuleBuffer An array of pointer to capsule images + @param[in] FileSize An array of UINTN to capsule images size + @param[in] CapsuleNum The count of capsule images + @param[out] BlockDescriptors The block descriptors for the capsule images + + @retval EFI_SUCCESS The block descriptors for the capsule images are constructed. +**/ +EFI_STATUS +BuildGatherList ( + IN VOID **CapsuleBuffer, + IN UINTN *FileSize, + IN UINTN CapsuleNum, + OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors + ) +{ + EFI_STATUS Status; + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors1; + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors2; + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorPre; + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorsHeader; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr; + UINT8 *TempDataPtr; + UINTN SizeLeft; + UINTN Size; + INT32 Count; + INT32 Number; + UINTN Index; + + TempBlockPtr = NULL; + BlockDescriptors1 = NULL; + BlockDescriptors2 = NULL; + BlockDescriptorPre = NULL; + BlockDescriptorsHeader = NULL; + + for (Index = 0; Index < CapsuleNum; Index++) { + // + // Allocate memory for the descriptors. + // + if (NumberOfDescriptors == 1) { + Count = 2; + } else { + Count = (INT32)(NumberOfDescriptors + 2) / 2; + } + + Size = Count * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + BlockDescriptors1 = AllocateRuntimeZeroPool (Size); + if (BlockDescriptors1 == NULL) { + Print (L"CapsuleApp: failed to allocate memory for descriptors\n"); + Status = EFI_OUT_OF_RESOURCES; + goto ERREXIT; + } else { + Print (L"CapsuleApp: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1); + Print (L"CapsuleApp: capsule data starts at 0x%X with size 0x%X\n", (UINTN) CapsuleBuffer[Index], FileSize[Index]); + } + + // + // Record descriptor header + // + if (Index == 0) { + BlockDescriptorsHeader = BlockDescriptors1; + } + + if (BlockDescriptorPre != NULL) { + BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1; + BlockDescriptorPre->Length = 0; + } + + // + // Fill them in + // + TempBlockPtr = BlockDescriptors1; + TempDataPtr = CapsuleBuffer[Index]; + SizeLeft = FileSize[Index]; + for (Number = 0; (Number < Count - 1) && (SizeLeft != 0); Number++) { + // + // Divide remaining data in half + // + if (NumberOfDescriptors != 1) { + if (SizeLeft == 1) { + Size = 1; + } else { + Size = SizeLeft / 2; + } + } else { + Size = SizeLeft; + } + TempBlockPtr->Union.DataBlock = (UINTN)TempDataPtr; + TempBlockPtr->Length = Size; + Print (L"CapsuleApp: capsule block/size 0x%X/0x%X\n", (UINTN) TempDataPtr, Size); + SizeLeft -= Size; + TempDataPtr += Size; + TempBlockPtr++; + } + + // + // Allocate the second list, point the first block's last entry to point + // to this one, and fill this one in. Worst case is that the previous + // list only had one element that pointed here, so we need at least two + // elements -- one to point to all the data, another to terminate the list. + // + if ((NumberOfDescriptors != 1) && (SizeLeft != 0)) { + Count = (INT32)(NumberOfDescriptors + 2) - Count; + if (Count == 1) { + Count++; + } + + Size = Count * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR); + BlockDescriptors2 = AllocateRuntimeZeroPool (Size); + if (BlockDescriptors2 == NULL) { + Print (L"CapsuleApp: failed to allocate memory for descriptors\n"); + Status = EFI_OUT_OF_RESOURCES; + goto ERREXIT; + } + + // + // Point the first list's last element to point to this second list. + // + TempBlockPtr->Union.ContinuationPointer = (UINTN) BlockDescriptors2; + + TempBlockPtr->Length = 0; + TempBlockPtr = BlockDescriptors2; + for (Number = 0; Number < Count - 1; Number++) { + // + // If second-to-last one, then dump rest to this element + // + if (Number == (Count - 2)) { + Size = SizeLeft; + } else { + // + // Divide remaining data in half + // + if (SizeLeft == 1) { + Size = 1; + } else { + Size = SizeLeft / 2; + } + } + + TempBlockPtr->Union.DataBlock = (UINTN)TempDataPtr; + TempBlockPtr->Length = Size; + Print (L"CapsuleApp: capsule block/size 0x%X/0x%X\n", (UINTN) TempDataPtr, Size); + SizeLeft -= Size; + TempDataPtr += Size; + TempBlockPtr++; + if (SizeLeft == 0) { + break; + } + } + } + + BlockDescriptorPre = TempBlockPtr; + BlockDescriptors1 = NULL; + } + + // + // Null-terminate. + // + if (TempBlockPtr != NULL) { + TempBlockPtr->Union.ContinuationPointer = (UINTN)NULL; + TempBlockPtr->Length = 0; + *BlockDescriptors = BlockDescriptorsHeader; + } + + return EFI_SUCCESS; + +ERREXIT: + if (BlockDescriptors1 != NULL) { + FreePool(BlockDescriptors1); + } + + if (BlockDescriptors2 != NULL) { + FreePool(BlockDescriptors2); + } + + return Status; +} + +/** + Clear the Gather list for a list of capsule images. + + @param[in] BlockDescriptors The block descriptors for the capsule images + @param[in] CapsuleNum The count of capsule images +**/ +VOID +CleanGatherList ( + IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors, + IN UINTN CapsuleNum + ) +{ + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr1; + EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr2; + UINTN Index; + + if (BlockDescriptors != NULL) { + TempBlockPtr1 = BlockDescriptors; + while (1){ + TempBlockPtr = TempBlockPtr1; + for (Index = 0; Index < CapsuleNum; Index++) { + if (TempBlockPtr[Index].Length == 0) { + break; + } + } + + if (TempBlockPtr[Index].Union.ContinuationPointer == (UINTN)NULL) { + break; + } + + TempBlockPtr2 = (VOID *) ((UINTN) TempBlockPtr[Index].Union.ContinuationPointer); + FreePool(TempBlockPtr1); + TempBlockPtr1 = TempBlockPtr2; + } + } +} + +/** + Print APP usage. +**/ +VOID +PrintUsage ( + VOID + ) +{ + Print(L"CapsuleApp: usage\n"); + Print(L" CapsuleApp [-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 -O \n"); + Print(L" CapsuleApp -N -O \n"); + Print(L" CapsuleApp -D \n"); + Print(L" CapsuleApp -P GET -O \n"); + Print(L"Parameter:\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.\n"); + 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"); + Print(L" which is defined in UEFI specification.\n"); + Print(L" -P: Dump UEFI FMP protocol info, or get image with specified\n"); + 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"); + Print(L" with its ImageTypeId supported by the system,\n"); + Print(L" according to Windows Firmware Update document\n"); + Print(L" -O: Output new Capsule file name\n"); + 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"); +} + +/** + Update Capsule image. + + @param[in] ImageHandle The image handle. + @param[in] SystemTable The system table. + + @retval EFI_SUCCESS Command completed successfully. + @retval EFI_UNSUPPORTED Command usage unsupported. + @retval EFI_INVALID_PARAMETER Command usage invalid. + @retval EFI_NOT_FOUND The input file can't be found. +**/ +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + RETURN_STATUS RStatus; + UINTN CapsuleBufferSize[MAX_CAPSULE_NUM]; + VOID *CapsuleBuffer[MAX_CAPSULE_NUM]; + EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors; + EFI_CAPSULE_HEADER *CapsuleHeaderArray[MAX_CAPSULE_NUM + 1]; + UINT64 MaxCapsuleSize; + 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; + + BlockDescriptors = NULL; + MapFsStr = NULL; + CapsuleNum = 0; + + Status = GetArg(); + if (EFI_ERROR(Status)) { + Print(L"Please use UEFI SHELL to run this application!\n", Status); + return Status; + } + if (Argc < 2) { + PrintUsage(); + return EFI_UNSUPPORTED; + } + if (StrCmp(Argv[1], L"-D") == 0) { + if (Argc != 3) { + Print(L"CapsuleApp: Incorrect parameter count.\n"); + return EFI_UNSUPPORTED; + } + Status = DumpCapsule(Argv[2]); + return Status; + } + if (StrCmp(Argv[1], L"-G") == 0) { + Status = CreateBmpFmp(); + return Status; + } + if (StrCmp(Argv[1], L"-N") == 0) { + Status = CreateNestedFmp(); + return Status; + } + if (StrCmp(Argv[1], L"-S") == 0) { + Status = DumpCapsuleStatusVariable(); + return EFI_SUCCESS; + } + if (StrCmp(Argv[1], L"-C") == 0) { + Status = ClearCapsuleStatusVariable(); + return Status; + } + if (StrCmp(Argv[1], L"-P") == 0) { + if (Argc == 2) { + DumpFmpData(); + } + if (Argc >= 3) { + if (StrCmp(Argv[2], L"GET") != 0) { + Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[2]); + return EFI_UNSUPPORTED; + } else { + if (Argc != 7) { + Print(L"CapsuleApp: Incorrect parameter count.\n"); + return EFI_UNSUPPORTED; + } + + // + // FMP->GetImage() + // + RStatus = StrToGuid (Argv[3], &ImageTypeId); + if (RETURN_ERROR (RStatus) || (Argv[3][GUID_STRING_LENGTH] != L'\0')) { + Print (L"Invalid ImageTypeId - %s\n", Argv[3]); + return EFI_INVALID_PARAMETER; + } + ImageIndex = StrDecimalToUintn(Argv[4]); + if (StrCmp(Argv[5], L"-O") != 0) { + Print(L"CapsuleApp: NO output file name.\n"); + return EFI_UNSUPPORTED; + } + DumpFmpImage(&ImageTypeId, ImageIndex, Argv[6]); + } + } + return EFI_SUCCESS; + } + + if (StrCmp(Argv[1], L"-E") == 0) { + DumpEsrtData(); + 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; + } + + CapsuleFirstIndex = 1; + NoReset = FALSE; + 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 > ParaNrIndex) { + if (ParaNrIndex != 0) { + CapsuleLastIndex = ParaNrIndex - 1; + } else { + CapsuleLastIndex = ParaOdIndex - 1; + } + + if (ParaOdIndex == Argc -1) { + MapFsStr = NULL; + } else if (ParaOdIndex == Argc - 2) { + MapFsStr = Argv[Argc-1]; + } else { + Print (L"CapsuleApp: Cannot specify more than one FS mapping!\n"); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + } else if (ParaOdIndex < ParaNrIndex) { + if (ParaOdIndex != 0) { + CapsuleLastIndex = ParaOdIndex - 1; + if (ParaOdIndex == ParaNrIndex - 1) { + MapFsStr = NULL; + } else if (ParaOdIndex == ParaNrIndex - 2) { + MapFsStr = Argv[ParaOdIndex + 1]; + } else { + Print (L"CapsuleApp: Cannot specify more than one FS mapping!\n"); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + } else { + CapsuleLastIndex = ParaNrIndex - 1; + } + } else { + CapsuleLastIndex = Argc - 1; + } + + CapsuleNum = CapsuleLastIndex - CapsuleFirstIndex + 1; + + if (CapsuleFirstIndex > CapsuleLastIndex) { + Print(L"CapsuleApp: NO capsule image.\n"); + return EFI_UNSUPPORTED; + } + if (CapsuleNum > MAX_CAPSULE_NUM) { + Print(L"CapsuleApp: Too many capsule images.\n"); + return EFI_UNSUPPORTED; + } + + ZeroMem(&CapsuleBuffer, sizeof(CapsuleBuffer)); + ZeroMem(&CapsuleBufferSize, sizeof(CapsuleBufferSize)); + BlockDescriptors = NULL; + + for (Index = 0; Index < CapsuleNum; Index++) { + CapsuleName = Argv[CapsuleFirstIndex + 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], 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, CapsuleBufferSize, CapsuleNum, &BlockDescriptors); + if (EFI_ERROR(Status)) { + goto Done; + } + + // + // Call the runtime service capsule. + // + NeedReset = FALSE; + for (Index = 0; Index < CapsuleNum; Index++) { + CapsuleHeaderArray[Index] = (EFI_CAPSULE_HEADER *) CapsuleBuffer[Index]; + if ((CapsuleHeaderArray[Index]->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) { + NeedReset = TRUE; + } + } + CapsuleHeaderArray[CapsuleNum] = NULL; + + // + // Inquire platform capability of UpdateCapsule. + // + Status = gRT->QueryCapsuleCapabilities (CapsuleHeaderArray, CapsuleNum, &MaxCapsuleSize, &ResetType); + if (EFI_ERROR(Status)) { + Print (L"CapsuleApp: failed to query capsule capability - %r\n", Status); + goto Done; + } + + for (Index = 0; Index < CapsuleNum; Index++) { + 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. + // + if (NeedReset) { + Status = gRT->UpdateCapsule(CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors); + if (Status != EFI_SUCCESS) { + Print (L"CapsuleApp: failed to update capsule - %r\n", Status); + goto Done; + } + // + // For capsule with CAPSULE_FLAGS_PERSIST_ACROSS_RESET + CAPSULE_FLAGS_INITIATE_RESET, + // a system reset should have been triggered by gRT->UpdateCapsule() calling above. + // + // For capsule with CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without CAPSULE_FLAGS_INITIATE_RESET, + // check if -NR (no-reset) has been specified or not. + // + if (!NoReset) { + // + // For capsule who has reset flag and no -NR (no-reset) has been specified, after calling UpdateCapsule service, + // trigger a system reset to process capsule persist across a system reset. + // + gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL); + } + } else { + // + // For capsule who has no reset flag, only call UpdateCapsule Service without a + // system reset. The service will process the capsule immediately. + // + Status = gRT->UpdateCapsule (CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors); + if (Status != EFI_SUCCESS) { + Print (L"CapsuleApp: failed to update capsule - %r\n", Status); + } + } + + Status = EFI_SUCCESS; + +Done: + for (Index = 0; Index < CapsuleNum; Index++) { + if (CapsuleBuffer[Index] != NULL) { + FreePool (CapsuleBuffer[Index]); + } + } + + CleanGatherList(BlockDescriptors, CapsuleNum); + + return Status; +} diff --git a/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.h b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.h new file mode 100644 index 000000000..270d2359a --- /dev/null +++ b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.h @@ -0,0 +1,240 @@ +/** @file + A shell application that triggers capsule update process. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + + +#ifndef _CAPSULE_APP_H_ +#define _CAPSULE_APP_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CAPSULE_HEADER_SIZE 0x20 + +#define NESTED_CAPSULE_HEADER_SIZE SIZE_4KB +#define SYSTEM_FIRMWARE_FLAG 0x50000 +#define DEVICE_FIRMWARE_FLAG 0x78010 + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 0 + +#define MAX_CAPSULE_NUM 10 + +// +// (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)) + +extern UINTN Argc; +extern CHAR16 **Argv; + +/** + + This function parse application ARG. + + @return Status +**/ +EFI_STATUS +GetArg ( + VOID + ); + +/** + Get shell protocol. + + @return Pointer to shell protocol. + +**/ +EFI_SHELL_PROTOCOL * +GetShellProtocol ( + VOID + ); + + +/** + Read a file. + + @param[in] FileName The file to be read. + @param[out] BufferSize The file buffer size + @param[out] Buffer The file buffer + + @retval EFI_SUCCESS Read file successfully + @retval EFI_NOT_FOUND Shell protocol or file not found + @retval others Read file failed +**/ +EFI_STATUS +ReadFileToBuffer ( + IN CHAR16 *FileName, + OUT UINTN *BufferSize, + OUT VOID **Buffer + ); + +/** + Write a file. + + @param[in] FileName The file to be written. + @param[in] BufferSize The file buffer size + @param[in] Buffer The file buffer + + @retval EFI_SUCCESS Write file successfully + @retval EFI_NOT_FOUND Shell protocol not found + @retval others Write file failed +**/ +EFI_STATUS +WriteFileFromBuffer ( + IN CHAR16 *FileName, + IN UINTN BufferSize, + IN VOID *Buffer + ); + + +/** + Dump capsule information + + @param[in] CapsuleName The name of the capsule image. + + @retval EFI_SUCCESS The capsule information is dumped. + @retval EFI_UNSUPPORTED Input parameter is not valid. +**/ +EFI_STATUS +DumpCapsule ( + IN CHAR16 *CapsuleName + ); + +/** + Dump capsule status variable. + + @retval EFI_SUCCESS The capsule status variable is dumped. + @retval EFI_UNSUPPORTED Input parameter is not valid. +**/ +EFI_STATUS +DumpCapsuleStatusVariable ( + VOID + ); + +/** + Dump FMP protocol info. +**/ +VOID +DumpFmpData ( + VOID + ); + +/** + Dump FMP image data. + + @param[in] ImageTypeId The ImageTypeId of the FMP image. + It is used to identify the FMP protocol. + @param[in] ImageIndex The ImageIndex of the FMP image. + It is the input parameter for FMP->GetImage(). + @param[in] ImageName The file name to hold the output FMP image. +**/ +VOID +DumpFmpImage ( + IN EFI_GUID *ImageTypeId, + IN UINTN ImageIndex, + IN CHAR16 *ImageName + ); + +/** + Dump ESRT info. +**/ +VOID +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 Partition. +**/ +VOID +DumpAllEfiSysPartition ( + 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 +GetEfiSysPartitionFromBootOptionFilePath ( + IN EFI_DEVICE_PATH_PROTOCOL *DevicePath, + OUT EFI_DEVICE_PATH_PROTOCOL **FullPath, + OUT EFI_SIMPLE_FILE_SYSTEM_PROTOCOL **Fs + ); + + +/** + Process Capsule On Disk. + + @param[in] CapsuleBuffer An array of pointer to capsule images + @param[in] CapsuleBufferSize An array of UINTN to capsule images size + @param[in] FilePath An array of capsule images file path + @param[in] Map File system mapping string + @param[in] CapsuleNum The count of capsule images + + @retval EFI_SUCCESS Capsule on disk success. + @retval others Capsule on disk fail. + +**/ +EFI_STATUS +ProcessCapsuleOnDisk ( + IN VOID **CapsuleBuffer, + IN UINTN *CapsuleBufferSize, + IN CHAR16 **FilePath, + IN CHAR16 *Map, + IN UINTN CapsuleNum + ); + +#endif + diff --git a/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf new file mode 100644 index 000000000..6ed065983 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf @@ -0,0 +1,69 @@ +## @file +# A shell application that triggers capsule update process. +# +# This application can trigger capsule update process. It can also +# generate capsule image, or dump capsule variable information. +# +# Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010006 + BASE_NAME = CapsuleApp + MODULE_UNI_FILE = CapsuleApp.uni + FILE_GUID = 4CEF31DA-8682-4274-9CC4-AEE7516A5E7B + MODULE_TYPE = UEFI_APPLICATION + VERSION_STRING = 1.0 + ENTRY_POINT = UefiMain + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = IA32 X64 +# + +[Sources] + CapsuleApp.c + CapsuleApp.h + CapsuleDump.c + CapsuleOnDisk.c + AppSupport.c + +[Packages] + MdePkg/MdePkg.dec + 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 + UefiApplicationEntryPoint + DebugLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiRuntimeServicesTableLib + UefiLib + PrintLib + BmpSupportLib + FileHandleLib + UefiBootManagerLib + SortLib + +[UserExtensions.TianoCore."ExtraFiles"] + CapsuleAppExtra.uni diff --git a/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.uni b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.uni new file mode 100644 index 000000000..955979dcc --- /dev/null +++ b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleApp.uni @@ -0,0 +1,17 @@ +// /** @file +// A shell application that triggers capsule update process. +// +// This application can trigger capsule update process. It can also +// generate capsule image, or dump capsule variable information. +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + + +#string STR_MODULE_ABSTRACT #language en-US "A shell application that triggers capsule update process." + +#string STR_MODULE_DESCRIPTION #language en-US "This application can trigger capsule update process. It can also generate capsule image, or dump capsule variable information." + diff --git a/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleAppExtra.uni b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleAppExtra.uni new file mode 100644 index 000000000..42bd212a9 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleAppExtra.uni @@ -0,0 +1,14 @@ +// /** @file +// CapsuleApp Localized Strings and Content +// +// Copyright (c) 2016, Intel Corporation. All rights reserved.
+// +// SPDX-License-Identifier: BSD-2-Clause-Patent +// +// **/ + +#string STR_PROPERTIES_MODULE_NAME +#language en-US +"Capsule Application" + + diff --git a/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c new file mode 100644 index 000000000..5725e2f6d --- /dev/null +++ b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleDump.c @@ -0,0 +1,1444 @@ +/** @file + Dump Capsule image information. + + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleApp.h" + +/** + Validate if it is valid capsule header + + This function assumes the caller provided correct CapsuleHeader pointer + and CapsuleSize. + + This function validates the fields in EFI_CAPSULE_HEADER. + + @param[in] CapsuleHeader Points to a capsule header. + @param[in] CapsuleSize Size of the whole capsule image. + +**/ +BOOLEAN +IsValidCapsuleHeader ( + IN EFI_CAPSULE_HEADER *CapsuleHeader, + IN UINT64 CapsuleSize + ); + +/** + Dump UX capsule information. + + @param[in] CapsuleHeader The UX capsule header +**/ +VOID +DumpUxCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + EFI_DISPLAY_CAPSULE *DisplayCapsule; + DisplayCapsule = (EFI_DISPLAY_CAPSULE *)CapsuleHeader; + Print(L"[UxCapsule]\n"); + Print(L"CapsuleHeader:\n"); + Print(L" CapsuleGuid - %g\n", &DisplayCapsule->CapsuleHeader.CapsuleGuid); + Print(L" HeaderSize - 0x%x\n", DisplayCapsule->CapsuleHeader.HeaderSize); + Print(L" Flags - 0x%x\n", DisplayCapsule->CapsuleHeader.Flags); + Print(L" CapsuleImageSize - 0x%x\n", DisplayCapsule->CapsuleHeader.CapsuleImageSize); + Print(L"ImagePayload:\n"); + Print(L" Version - 0x%x\n", DisplayCapsule->ImagePayload.Version); + Print(L" Checksum - 0x%x\n", DisplayCapsule->ImagePayload.Checksum); + Print(L" ImageType - 0x%x\n", DisplayCapsule->ImagePayload.ImageType); + Print(L" Mode - 0x%x\n", DisplayCapsule->ImagePayload.Mode); + Print(L" OffsetX - 0x%x\n", DisplayCapsule->ImagePayload.OffsetX); + Print(L" OffsetY - 0x%x\n", DisplayCapsule->ImagePayload.OffsetY); +} + + +/** + Dump a non-nested FMP capsule. + + @param[in] CapsuleHeader A pointer to CapsuleHeader +**/ +VOID +DumpFmpCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader; + UINT64 *ItemOffsetList; + UINTN Index; + UINTN Count; + EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *FmpImageHeader; + + Print(L"[FmpCapsule]\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); + + FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize); + ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1); + Print(L"FmpHeader:\n"); + Print(L" Version - 0x%x\n", FmpCapsuleHeader->Version); + Print(L" EmbeddedDriverCount - 0x%x\n", FmpCapsuleHeader->EmbeddedDriverCount); + Print(L" PayloadItemCount - 0x%x\n", FmpCapsuleHeader->PayloadItemCount); + Count = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount; + for (Index = 0; Index < Count; Index++) { + Print(L" Offset[%d] - 0x%x\n", Index, ItemOffsetList[Index]); + } + + for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < Count; Index++) { + Print(L"FmpPayload[%d] ImageHeader:\n", Index); + FmpImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]); + Print(L" Version - 0x%x\n", FmpImageHeader->Version); + Print(L" UpdateImageTypeId - %g\n", &FmpImageHeader->UpdateImageTypeId); + Print(L" UpdateImageIndex - 0x%x\n", FmpImageHeader->UpdateImageIndex); + Print(L" UpdateImageSize - 0x%x\n", FmpImageHeader->UpdateImageSize); + Print(L" UpdateVendorCodeSize - 0x%x\n", FmpImageHeader->UpdateVendorCodeSize); + if (FmpImageHeader->Version >= 2) { + Print(L" UpdateHardwareInstance - 0x%lx\n", FmpImageHeader->UpdateHardwareInstance); + if (FmpImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) { + Print(L" ImageCapsuleSupport - 0x%lx\n", FmpImageHeader->ImageCapsuleSupport); + } + } + } +} + +/** + Return if there is a FMP header below capsule header. + + @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER + + @retval TRUE There is a FMP header below capsule header. + @retval FALSE There is not a FMP header below capsule header +**/ +BOOLEAN +IsNestedFmpCapsule ( + IN EFI_CAPSULE_HEADER *CapsuleHeader + ) +{ + EFI_STATUS Status; + EFI_SYSTEM_RESOURCE_TABLE *Esrt; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry; + UINTN Index; + BOOLEAN EsrtGuidFound; + EFI_CAPSULE_HEADER *NestedCapsuleHeader; + UINTN NestedCapsuleSize; + + // + // Check ESRT + // + EsrtGuidFound = FALSE; + Status = EfiGetSystemConfigurationTable(&gEfiSystemResourceTableGuid, (VOID **)&Esrt); + if (!EFI_ERROR(Status)) { + ASSERT (Esrt != NULL); + EsrtEntry = (VOID *)(Esrt + 1); + for (Index = 0; Index < Esrt->FwResourceCount; Index++, EsrtEntry++) { + if (CompareGuid(&EsrtEntry->FwClass, &CapsuleHeader->CapsuleGuid)) { + EsrtGuidFound = TRUE; + break; + } + } + } + + if (!EsrtGuidFound) { + return FALSE; + } + + // + // Check nested capsule header + // FMP GUID after ESRT one + // + NestedCapsuleHeader = (EFI_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize); + NestedCapsuleSize = (UINTN)CapsuleHeader + CapsuleHeader->CapsuleImageSize- (UINTN)NestedCapsuleHeader; + if (NestedCapsuleSize < sizeof(EFI_CAPSULE_HEADER)) { + return FALSE; + } + if (!CompareGuid(&NestedCapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)) { + return FALSE; + } + return TRUE; +} + +/** + Dump capsule information + + @param[in] CapsuleName The name of the capsule image. + + @retval EFI_SUCCESS The capsule information is dumped. + @retval EFI_UNSUPPORTED Input parameter is not valid. +**/ +EFI_STATUS +DumpCapsule ( + IN CHAR16 *CapsuleName + ) +{ + VOID *Buffer; + UINTN FileSize; + EFI_CAPSULE_HEADER *CapsuleHeader; + EFI_STATUS Status; + + Buffer = NULL; + Status = ReadFileToBuffer(CapsuleName, &FileSize, &Buffer); + if (EFI_ERROR(Status)) { + Print(L"CapsuleApp: Capsule (%s) is not found.\n", CapsuleName); + goto Done; + } + if (!IsValidCapsuleHeader (Buffer, FileSize)) { + Print(L"CapsuleApp: Capsule image (%s) is not a valid capsule.\n", CapsuleName); + Status = EFI_INVALID_PARAMETER; + goto Done; + } + + CapsuleHeader = Buffer; + if (CompareGuid(&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) { + DumpUxCapsule(CapsuleHeader); + Status = EFI_SUCCESS; + goto Done; + } + + if (CompareGuid(&CapsuleHeader->CapsuleGuid, &gEfiFmpCapsuleGuid)) { + DumpFmpCapsule(CapsuleHeader); + } + if (IsNestedFmpCapsule(CapsuleHeader)) { + Print(L"[NestedCapsule]\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)); + } + +Done: + if (Buffer != NULL) { + FreePool(Buffer); + } + return Status; +} + +/** + Dump capsule status variable. + + @retval EFI_SUCCESS The capsule status variable is dumped. + @retval EFI_UNSUPPORTED Input parameter is not valid. +**/ +EFI_STATUS +DumpCapsuleStatusVariable ( + VOID + ) +{ + EFI_STATUS Status; + UINT32 Index; + CHAR16 CapsuleVarName[20]; + CHAR16 *TempVarName; + EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResult; + EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultFmp; + UINTN CapsuleFileNameSize; + CHAR16 CapsuleIndexData[12]; + CHAR16 *CapsuleIndex; + CHAR16 *CapsuleFileName; + CHAR16 *CapsuleTarget; + + Status = GetVariable2( + L"CapsuleMax", + &gEfiCapsuleReportGuid, + (VOID **)&CapsuleIndex, + NULL + ); + if (!EFI_ERROR(Status)) { + ASSERT (CapsuleIndex != NULL); + CopyMem(CapsuleIndexData, CapsuleIndex, 11 * sizeof(CHAR16)); + CapsuleIndexData[11] = 0; + Print(L"CapsuleMax - %s\n", CapsuleIndexData); + FreePool(CapsuleIndex); + } + Status = GetVariable2( + L"CapsuleLast", + &gEfiCapsuleReportGuid, + (VOID **)&CapsuleIndex, + NULL + ); + if (!EFI_ERROR(Status)) { + ASSERT (CapsuleIndex != NULL); + CopyMem(CapsuleIndexData, CapsuleIndex, 11 * sizeof(CHAR16)); + CapsuleIndexData[11] = 0; + Print(L"CapsuleLast - %s\n", CapsuleIndexData); + FreePool(CapsuleIndex); + } + + + StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), L"Capsule"); + TempVarName = CapsuleVarName + StrLen (CapsuleVarName); + Index = 0; + + while (TRUE) { + UnicodeSPrint (TempVarName, 5 * sizeof(CHAR16), L"%04x", Index); + + Status = GetVariable2 ( + CapsuleVarName, + &gEfiCapsuleReportGuid, + (VOID **) &CapsuleResult, + NULL + ); + if (Status == EFI_NOT_FOUND) { + break; + } else if (EFI_ERROR(Status)) { + continue; + } + ASSERT (CapsuleResult != NULL); + + // + // display capsule process status + // + if (CapsuleResult->VariableTotalSize >= sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER)) { + Print (L"CapsuleName: %s\n", CapsuleVarName); + Print (L" Capsule Guid: %g\n", &CapsuleResult->CapsuleGuid); + Print (L" Capsule ProcessedTime: %t\n", &CapsuleResult->CapsuleProcessed); + Print (L" Capsule Status: %r\n", CapsuleResult->CapsuleStatus); + } + + if (CompareGuid(&CapsuleResult->CapsuleGuid, &gEfiFmpCapsuleGuid)) { + if (CapsuleResult->VariableTotalSize >= sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) * 2) { + CapsuleResultFmp = (EFI_CAPSULE_RESULT_VARIABLE_FMP *)(CapsuleResult + 1); + Print(L" Capsule FMP Version: 0x%x\n", CapsuleResultFmp->Version); + Print(L" Capsule FMP PayloadIndex: 0x%x\n", CapsuleResultFmp->PayloadIndex); + Print(L" Capsule FMP UpdateImageIndex: 0x%x\n", CapsuleResultFmp->UpdateImageIndex); + Print(L" Capsule FMP UpdateImageTypeId: %g\n", &CapsuleResultFmp->UpdateImageTypeId); + CapsuleFileName = (CHAR16 *)(CapsuleResultFmp + 1); + Print(L" Capsule FMP CapsuleFileName: \"%s\"\n", CapsuleFileName); + CapsuleFileNameSize = StrSize(CapsuleFileName); + CapsuleTarget = (CHAR16 *)((UINTN)CapsuleFileName + CapsuleFileNameSize); + Print(L" Capsule FMP CapsuleTarget: \"%s\"\n", CapsuleTarget); + } + } + + FreePool(CapsuleResult); + + Index++; + if (Index > 0xFFFF) { + break; + } + } + + return EFI_SUCCESS; +} + +CHAR8 *mFwTypeString[] = { + "Unknown", + "SystemFirmware", + "DeviceFirmware", + "UefiDriver", +}; + +CHAR8 *mLastAttemptStatusString[] = { + "Success", + "Error: Unsuccessful", + "Error: Insufficient Resources", + "Error: Incorrect Version", + "Error: Invalid Format", + "Error: Auth Error", + "Error: Power Event AC", + "Error: Power Event Battery", + "Error: Unsatisfied Dependencies", +}; + +/** + Convert FwType to a string. + + @param[in] FwType FwType in ESRT + + @return a string for FwType. +**/ +CHAR8 * +FwTypeToString ( + IN UINT32 FwType + ) +{ + if (FwType < sizeof(mFwTypeString) / sizeof(mFwTypeString[0])) { + return mFwTypeString[FwType]; + } else { + return "Invalid"; + } +} + +/** + Convert LastAttemptStatus to a string. + + @param[in] LastAttemptStatus LastAttemptStatus in FMP or ESRT + + @return a string for LastAttemptStatus. +**/ +CHAR8 * +LastAttemptStatusToString ( + IN UINT32 LastAttemptStatus + ) +{ + if (LastAttemptStatus < sizeof(mLastAttemptStatusString) / sizeof(mLastAttemptStatusString[0])) { + return mLastAttemptStatusString[LastAttemptStatus]; + } else { + return "Error: Unknown"; + } +} + +/** + Dump ESRT entry. + + @param[in] EsrtEntry ESRT entry +**/ +VOID +DumpEsrtEntry ( + IN EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry + ) +{ + Print(L" FwClass - %g\n", &EsrtEntry->FwClass); + Print(L" FwType - 0x%x (%a)\n", EsrtEntry->FwType, FwTypeToString(EsrtEntry->FwType)); + Print(L" FwVersion - 0x%x\n", EsrtEntry->FwVersion); + Print(L" LowestSupportedFwVersion - 0x%x\n", EsrtEntry->LowestSupportedFwVersion); + Print(L" CapsuleFlags - 0x%x\n", EsrtEntry->CapsuleFlags); + Print(L" LastAttemptVersion - 0x%x\n", EsrtEntry->LastAttemptVersion); + Print(L" LastAttemptStatus - 0x%x (%a)\n", EsrtEntry->LastAttemptStatus, LastAttemptStatusToString(EsrtEntry->LastAttemptStatus)); +} + +/** + Dump ESRT table. + + @param[in] Esrt ESRT table +**/ +VOID +DumpEsrt ( + IN EFI_SYSTEM_RESOURCE_TABLE *Esrt + ) +{ + UINTN Index; + EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry; + + if (Esrt == NULL) { + return ; + } + + Print(L"EFI_SYSTEM_RESOURCE_TABLE:\n"); + Print(L"FwResourceCount - 0x%x\n", Esrt->FwResourceCount); + Print(L"FwResourceCountMax - 0x%x\n", Esrt->FwResourceCountMax); + Print(L"FwResourceVersion - 0x%lx\n", Esrt->FwResourceVersion); + + EsrtEntry = (VOID *)(Esrt + 1); + for (Index = 0; Index < Esrt->FwResourceCount; Index++) { + Print(L"EFI_SYSTEM_RESOURCE_ENTRY (%d):\n", Index); + DumpEsrtEntry(EsrtEntry); + EsrtEntry++; + } +} + +/** + Dump ESRT info. +**/ +VOID +DumpEsrtData ( + VOID + ) +{ + EFI_STATUS Status; + EFI_SYSTEM_RESOURCE_TABLE *Esrt; + + Print(L"##############\n"); + Print(L"# ESRT TABLE #\n"); + Print(L"##############\n"); + + Status = EfiGetSystemConfigurationTable (&gEfiSystemResourceTableGuid, (VOID **)&Esrt); + if (EFI_ERROR(Status)) { + Print(L"ESRT - %r\n", Status); + return; + } + DumpEsrt(Esrt); + 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 != L'\0'; Cptr++) { + if (L'a' <= *Cptr && *Cptr <= L'z') { + *Cptr = *Cptr - L'a' + L'A'; + } + } + + return Str; +} + +/** + This routine is used to return substring before period '.' or '\0' + Caller should respsonsible of substr space allocation & free + + @param[in] Str String to check + @param[out] SubStr First part of string before period or '\0' + @param[out] SubStrLen Length of first part of string + +**/ +STATIC +VOID +GetSubStringBeforePeriod ( + IN CHAR16 *Str, + OUT CHAR16 *SubStr, + OUT UINTN *SubStrLen + ) +{ + UINTN Index; + for (Index = 0; Str[Index] != L'.' && Str[Index] != L'\0'; Index++) { + SubStr[Index] = Str[Index]; + } + + SubStr[Index] = L'\0'; + *SubStrLen = Index; +} + +/** + This routine pad the string in tail with input character. + + @param[in] StrBuf Str buffer to be padded, should be enough room for + @param[in] PadLen Expected padding length + @param[in] Character Character used to pad + +**/ +STATIC +VOID +PadStrInTail ( + IN CHAR16 *StrBuf, + IN UINTN PadLen, + IN CHAR16 Character + ) +{ + UINTN Index; + + for (Index = 0; StrBuf[Index] != L'\0'; Index++); + + while(PadLen != 0) { + StrBuf[Index] = Character; + Index++; + PadLen--; + } + + StrBuf[Index] = L'\0'; +} + +/** + This routine find the offset of the last period '.' of string. if No period exists + function FileNameExtension is set to L'\0' + + @param[in] FileName File name to split between last period + @param[out] FileNameFirst First FileName before last period + @param[out] FileNameExtension FileName after last period + +**/ +STATIC +VOID +SplitFileNameExtension ( + IN CHAR16 *FileName, + OUT CHAR16 *FileNameFirst, + OUT CHAR16 *FileNameExtension + ) +{ + UINTN Index; + UINTN StringLen; + + StringLen = 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 +CompareFileNameInAlphabet ( + IN VOID *Left, + IN VOID *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 *) (*(UINTN *)Left); + FileInfo2 = (EFI_FILE_INFO *) (*(UINTN *)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] Fs 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; + FileInfoBuffer = NULL; + FileInfo = NULL; + FileCount = 0; + NoFile = FALSE; + + Status = Fs->OpenVolume (Fs, &Root); + if (EFI_ERROR (Status)) { + Print (L"Cannot open volume. Status = %r\n", Status); + goto Done; + } + + Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FILE_DIRECTORY, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0); + if (EFI_ERROR (Status)) { + Print (L"Cannot open %s. Status = %r\n", EFI_CAPSULE_FILE_DIRECTORY, Status); + goto Done; + } + + // + // Get file count first + // + Status = FileHandleFindFirstFile (DirHandle, &FileInfo); + do { + if (EFI_ERROR (Status) || FileInfo == NULL) { + Print (L"Get File Info Fail. Status = %r\n", Status); + goto Done; + } + + if ((FileInfo->Attribute & (EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE)) != 0) { + FileCount++; + } + + Status = FileHandleFindNextFile (DirHandle, FileInfo, &NoFile); + if (EFI_ERROR (Status)) { + Print (L"Get Next File Fail. Status = %r\n", Status); + goto Done; + } + } while (!NoFile); + + if (FileCount == 0) { + Print (L"Error: No capsule file found!\n"); + Status = EFI_NOT_FOUND; + goto Done; + } + + FileInfoBuffer = AllocateZeroPool (sizeof (FileInfo) * FileCount); + if (FileInfoBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + NoFile = FALSE; + + // + // Get all file info + // + Status = FileHandleFindFirstFile (DirHandle, &FileInfo); + do { + if (EFI_ERROR (Status) || FileInfo == NULL) { + Print (L"Get File Info Fail. Status = %r\n", Status); + goto Done; + } + + if ((FileInfo->Attribute & (EFI_FILE_SYSTEM | EFI_FILE_ARCHIVE)) != 0) { + FileInfoBuffer[Index++] = AllocateCopyPool ((UINTN)FileInfo->Size, FileInfo); + } + + Status = FileHandleFindNextFile (DirHandle, FileInfo, &NoFile); + if (EFI_ERROR (Status)) { + Print (L"Get Next File Fail. Status = %r\n", Status); + goto Done; + } + } while (!NoFile); + + // + // 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) { + Status = EFI_SUCCESS; + goto Done; + } + + 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)) { + goto Done; + } + + Status = FileHandleGetSize (FileHandle, (UINT64 *) &FileSize); + if (EFI_ERROR (Status)) { + Print (L"Cannot read file %s. Status = %r\n", FileInfoBuffer[Index]->FileName, Status); + FileHandleClose (FileHandle); + goto Done; + } + + FileBuffer = AllocatePool (FileSize); + if (FileBuffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Done; + } + + Status = FileHandleRead (FileHandle, &FileSize, FileBuffer); + if (EFI_ERROR (Status)) { + Print (L"Cannot read file %s. Status = %r\n", FileInfoBuffer[Index]->FileName, Status); + FileHandleClose (FileHandle); + FreePool (FileBuffer); + goto Done; + } + + 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); + } + +Done: + if (FileInfoBuffer != NULL) { + for (Index = 0; Index < FileCount; Index++) { + if (FileInfoBuffer[Index] != NULL) { + FreePool (FileInfoBuffer[Index]); + } + } + FreePool (FileInfoBuffer); + } + + return Status; +} + +/** + 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; + + Index = 0; + CapsuleDataPtr64 = NULL; + BootNext = NULL; + + ShellProtocol = GetShellProtocol (); + if (ShellProtocol == NULL) { + Print (L"Get Shell Protocol Fail\n"); + return ; + } + + // + // 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) || CapsuleDataPtr64 == NULL) { + if (Index == 0) { + Print (L"No data.\n"); + } + break; + } + + 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) || BootNext == NULL) { + Print (L"Get BootNext Variable Fail. Status = %r\n", Status); + } else { + 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. + + @param[in] ImageInfoSize The size of ImageInfo, in bytes. + @param[in] ImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] DescriptorVersion The version of EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] DescriptorCount The count of EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] DescriptorSize The size of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR, in bytes. + @param[in] PackageVersion The version of package. + @param[in] PackageVersionName The version name of package. +**/ +VOID +DumpFmpImageInfo ( + IN UINTN ImageInfoSize, + IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo, + IN UINT32 DescriptorVersion, + IN UINT8 DescriptorCount, + IN UINTN DescriptorSize, + IN UINT32 PackageVersion, + IN CHAR16 *PackageVersionName + ) +{ + EFI_FIRMWARE_IMAGE_DESCRIPTOR *CurrentImageInfo; + UINTN Index; + UINTN Index2; + + Print(L" DescriptorVersion - 0x%x\n", DescriptorVersion); + Print(L" DescriptorCount - 0x%x\n", DescriptorCount); + Print(L" DescriptorSize - 0x%x\n", DescriptorSize); + Print(L" PackageVersion - 0x%x\n", PackageVersion); + Print(L" PackageVersionName - \"%s\"\n", PackageVersionName); + CurrentImageInfo = ImageInfo; + for (Index = 0; Index < DescriptorCount; Index++) { + Print(L" ImageDescriptor (%d)\n", Index); + Print(L" ImageIndex - 0x%x\n", CurrentImageInfo->ImageIndex); + Print(L" ImageTypeId - %g\n", &CurrentImageInfo->ImageTypeId); + Print(L" ImageId - 0x%lx\n", CurrentImageInfo->ImageId); + Print(L" ImageIdName - \"%s\"\n", CurrentImageInfo->ImageIdName); + Print(L" Version - 0x%x\n", CurrentImageInfo->Version); + Print(L" VersionName - \"%s\"\n", CurrentImageInfo->VersionName); + Print(L" Size - 0x%x\n", CurrentImageInfo->Size); + Print(L" AttributesSupported - 0x%lx\n", CurrentImageInfo->AttributesSupported); + Print(L" IMAGE_UPDATABLE - 0x%lx\n", CurrentImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_IMAGE_UPDATABLE); + Print(L" RESET_REQUIRED - 0x%lx\n", CurrentImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_RESET_REQUIRED); + Print(L" AUTHENTICATION_REQUIRED - 0x%lx\n", CurrentImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED); + Print(L" IN_USE - 0x%lx\n", CurrentImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_IN_USE); + Print(L" UEFI_IMAGE - 0x%lx\n", CurrentImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_UEFI_IMAGE); + Print(L" AttributesSetting - 0x%lx\n", CurrentImageInfo->AttributesSetting); + Print(L" IMAGE_UPDATABLE - 0x%lx\n", CurrentImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_IMAGE_UPDATABLE); + Print(L" RESET_REQUIRED - 0x%lx\n", CurrentImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_RESET_REQUIRED); + Print(L" AUTHENTICATION_REQUIRED - 0x%lx\n", CurrentImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED); + Print(L" IN_USE - 0x%lx\n", CurrentImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_IN_USE); + Print(L" UEFI_IMAGE - 0x%lx\n", CurrentImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_UEFI_IMAGE); + Print(L" Compatibilities - 0x%lx\n", CurrentImageInfo->Compatibilities); + Print(L" COMPATIB_CHECK_SUPPORTED - 0x%lx\n", CurrentImageInfo->Compatibilities & IMAGE_COMPATIBILITY_CHECK_SUPPORTED); + if (DescriptorVersion > 1) { + Print(L" LowestSupportedImageVersion - 0x%x\n", CurrentImageInfo->LowestSupportedImageVersion); + if (DescriptorVersion > 2) { + Print(L" LastAttemptVersion - 0x%x\n", CurrentImageInfo->LastAttemptVersion); + Print(L" LastAttemptStatus - 0x%x (%a)\n", CurrentImageInfo->LastAttemptStatus, LastAttemptStatusToString(CurrentImageInfo->LastAttemptStatus)); + Print(L" HardwareInstance - 0x%lx\n", CurrentImageInfo->HardwareInstance); + if (DescriptorVersion > 3) { + Print(L" Dependencies - "); + if (CurrentImageInfo->Dependencies == NULL) { + Print(L"NULL\n"); + } else { + Index2 = 0; + do { + Print(L"%02x ", CurrentImageInfo->Dependencies->Dependencies[Index2]); + } while (CurrentImageInfo->Dependencies->Dependencies[Index2 ++] != EFI_FMP_DEP_END); + Print(L"\n"); + } + } + } + } + // + // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version + // + CurrentImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)CurrentImageInfo + DescriptorSize); + } +} + +/** + Dump FMP package information. + + @param[in] PackageVersion The version of package. + @param[in] PackageVersionName The version name of package. + @param[in] PackageVersionNameMaxLen The maximum length of PackageVersionName. + @param[in] AttributesSupported Package attributes that are supported by this device. + @param[in] AttributesSetting Package attributes. +**/ +VOID +DumpFmpPackageInfo ( + IN UINT32 PackageVersion, + IN CHAR16 *PackageVersionName, + IN UINT32 PackageVersionNameMaxLen, + IN UINT64 AttributesSupported, + IN UINT64 AttributesSetting + ) +{ + Print(L" PackageVersion - 0x%x\n", PackageVersion); + Print(L" PackageVersionName - \"%s\"\n", PackageVersionName); + Print(L" PackageVersionNameMaxLen - 0x%x\n", PackageVersionNameMaxLen); + Print(L" AttributesSupported - 0x%lx\n", AttributesSupported); + Print(L" IMAGE_UPDATABLE - 0x%lx\n", AttributesSupported & IMAGE_ATTRIBUTE_IMAGE_UPDATABLE); + Print(L" RESET_REQUIRED - 0x%lx\n", AttributesSupported & IMAGE_ATTRIBUTE_RESET_REQUIRED); + Print(L" AUTHENTICATION_REQUIRED - 0x%lx\n", AttributesSupported & IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED); + Print(L" AttributesSetting - 0x%lx\n", AttributesSetting); + Print(L" IMAGE_UPDATABLE - 0x%lx\n", AttributesSetting & IMAGE_ATTRIBUTE_IMAGE_UPDATABLE); + Print(L" RESET_REQUIRED - 0x%lx\n", AttributesSetting & IMAGE_ATTRIBUTE_RESET_REQUIRED); + Print(L" AUTHENTICATION_REQUIRED - 0x%lx\n", AttributesSetting & IMAGE_ATTRIBUTE_AUTHENTICATION_REQUIRED); +} + +/** + Dump FMP protocol info. +**/ +VOID +DumpFmpData ( + VOID + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + EFI_HANDLE *HandleBuffer; + UINTN NumberOfHandles; + UINTN Index; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + UINTN ImageInfoSize; + UINT32 FmpImageInfoDescriptorVer; + UINT8 FmpImageInfoCount; + UINTN DescriptorSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + UINT32 PackageVersionNameMaxLen; + UINT64 AttributesSupported; + UINT64 AttributesSetting; + + Print(L"############\n"); + Print(L"# FMP DATA #\n"); + Print(L"############\n"); + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + if (EFI_ERROR(Status)) { + Print(L"FMP protocol - %r\n", EFI_NOT_FOUND); + return; + } + + for (Index = 0; Index < NumberOfHandles; Index++) { + Status = gBS->HandleProtocol( + HandleBuffer[Index], + &gEfiFirmwareManagementProtocolGuid, + (VOID **)&Fmp + ); + if (EFI_ERROR(Status)) { + continue; + } + + ImageInfoSize = 0; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + continue; + } + + FmpImageInfoBuf = NULL; + FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); + if (FmpImageInfoBuf == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto EXIT; + } + + PackageVersionName = NULL; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + &FmpImageInfoDescriptorVer, // DescriptorVersion + &FmpImageInfoCount, // DescriptorCount + &DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + + // + // If FMP GetInformation interface failed, skip this resource + // + if (EFI_ERROR(Status)) { + Print(L"FMP (%d) ImageInfo - %r\n", Index, Status); + FreePool(FmpImageInfoBuf); + continue; + } + + Print(L"FMP (%d) ImageInfo:\n", Index); + DumpFmpImageInfo( + ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + FmpImageInfoDescriptorVer, // DescriptorVersion + FmpImageInfoCount, // DescriptorCount + DescriptorSize, // DescriptorSize + PackageVersion, // PackageVersion + PackageVersionName // PackageVersionName + ); + + if (PackageVersionName != NULL) { + FreePool(PackageVersionName); + } + FreePool(FmpImageInfoBuf); + + // + // Get package info + // + PackageVersionName = NULL; + Status = Fmp->GetPackageInfo ( + Fmp, + &PackageVersion, // PackageVersion + &PackageVersionName, // PackageVersionName + &PackageVersionNameMaxLen, // PackageVersionNameMaxLen + &AttributesSupported, // AttributesSupported + &AttributesSetting // AttributesSetting + ); + if (EFI_ERROR(Status)) { + Print(L"FMP (%d) PackageInfo - %r\n", Index, Status); + } else { + Print(L"FMP (%d) ImageInfo:\n", Index); + DumpFmpPackageInfo( + PackageVersion, // PackageVersion + PackageVersionName, // PackageVersionName + PackageVersionNameMaxLen, // PackageVersionNameMaxLen + AttributesSupported, // AttributesSupported + AttributesSetting // AttributesSetting + ); + + if (PackageVersionName != NULL) { + FreePool(PackageVersionName); + } + } + } + Print(L"\n"); + +EXIT: + FreePool(HandleBuffer); +} + +/** + Check if the ImageInfo includes the ImageTypeId. + + @param[in] ImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] DescriptorCount The count of EFI_FIRMWARE_IMAGE_DESCRIPTOR. + @param[in] DescriptorSize The size of an individual EFI_FIRMWARE_IMAGE_DESCRIPTOR, in bytes. + @param[in] ImageTypeId A unique GUID identifying the firmware image type. + + @return TRUE This ImageInfo includes the ImageTypeId + @return FALSE This ImageInfo does not include the ImageTypeId +**/ +BOOLEAN +IsThisFmpImageInfo ( + IN EFI_FIRMWARE_IMAGE_DESCRIPTOR *ImageInfo, + IN UINT8 DescriptorCount, + IN UINTN DescriptorSize, + IN EFI_GUID *ImageTypeId + ) +{ + EFI_FIRMWARE_IMAGE_DESCRIPTOR *CurrentImageInfo; + UINTN Index; + + CurrentImageInfo = ImageInfo; + for (Index = 0; Index < DescriptorCount; Index++) { + if (CompareGuid (&CurrentImageInfo->ImageTypeId, ImageTypeId)) { + return TRUE; + } + CurrentImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)CurrentImageInfo + DescriptorSize); + } + return FALSE; +} + +/** + return the FMP whoes ImageInfo includes the ImageTypeId. + + @param[in] ImageTypeId A unique GUID identifying the firmware image type. + + @return The FMP whoes ImageInfo includes the ImageTypeId +**/ +EFI_FIRMWARE_MANAGEMENT_PROTOCOL * +FindFmpFromImageTypeId ( + IN EFI_GUID *ImageTypeId + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *TargetFmp; + EFI_HANDLE *HandleBuffer; + UINTN NumberOfHandles; + UINTN Index; + EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf; + UINTN ImageInfoSize; + UINT32 FmpImageInfoDescriptorVer; + UINT8 FmpImageInfoCount; + UINTN DescriptorSize; + UINT32 PackageVersion; + CHAR16 *PackageVersionName; + + Status = gBS->LocateHandleBuffer ( + ByProtocol, + &gEfiFirmwareManagementProtocolGuid, + NULL, + &NumberOfHandles, + &HandleBuffer + ); + if (EFI_ERROR(Status)) { + Print(L"FMP protocol - %r\n", EFI_NOT_FOUND); + return NULL; + } + + TargetFmp = NULL; + for (Index = 0; Index < NumberOfHandles; Index++) { + Status = gBS->HandleProtocol( + HandleBuffer[Index], + &gEfiFirmwareManagementProtocolGuid, + (VOID **)&Fmp + ); + if (EFI_ERROR(Status)) { + continue; + } + + ImageInfoSize = 0; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL + ); + if (Status != EFI_BUFFER_TOO_SMALL) { + continue; + } + + FmpImageInfoBuf = NULL; + FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize); + if (FmpImageInfoBuf == NULL) { + FreePool(HandleBuffer); + Print(L"Out of resource\n"); + return NULL; + } + + PackageVersionName = NULL; + Status = Fmp->GetImageInfo ( + Fmp, + &ImageInfoSize, // ImageInfoSize + FmpImageInfoBuf, // ImageInfo + &FmpImageInfoDescriptorVer, // DescriptorVersion + &FmpImageInfoCount, // DescriptorCount + &DescriptorSize, // DescriptorSize + &PackageVersion, // PackageVersion + &PackageVersionName // PackageVersionName + ); + + // + // If FMP GetInformation interface failed, skip this resource + // + if (EFI_ERROR(Status)) { + FreePool(FmpImageInfoBuf); + continue; + } + + if (PackageVersionName != NULL) { + FreePool(PackageVersionName); + } + + if (IsThisFmpImageInfo (FmpImageInfoBuf, FmpImageInfoCount, DescriptorSize, ImageTypeId)) { + TargetFmp = Fmp; + } + FreePool(FmpImageInfoBuf); + if (TargetFmp != NULL) { + break; + } + } + FreePool(HandleBuffer); + return TargetFmp; +} + +/** + Dump FMP image data. + + @param[in] ImageTypeId The ImageTypeId of the FMP image. + It is used to identify the FMP protocol. + @param[in] ImageIndex The ImageIndex of the FMP image. + It is the input parameter for FMP->GetImage(). + @param[in] ImageName The file name to hold the output FMP image. +**/ +VOID +DumpFmpImage ( + IN EFI_GUID *ImageTypeId, + IN UINTN ImageIndex, + IN CHAR16 *ImageName + ) +{ + EFI_STATUS Status; + EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp; + VOID *Image; + UINTN ImageSize; + + Fmp = FindFmpFromImageTypeId (ImageTypeId); + if (Fmp == NULL) { + Print(L"No FMP include ImageTypeId %g\n", ImageTypeId); + return ; + } + + if (ImageIndex > 0xFF) { + Print(L"ImageIndex 0x%x too big\n", ImageIndex); + return ; + } + + Image = Fmp; + ImageSize = 0; + Status = Fmp->GetImage (Fmp, (UINT8)ImageIndex, Image, &ImageSize); + if (Status != EFI_BUFFER_TOO_SMALL) { + Print(L"Fmp->GetImage - %r\n", Status); + return ; + } + + Image = AllocatePool (ImageSize); + if (Image == NULL) { + Print(L"Allocate FmpImage 0x%x - %r\n", ImageSize, EFI_OUT_OF_RESOURCES); + return ; + } + + Status = Fmp->GetImage (Fmp, (UINT8)ImageIndex, Image, &ImageSize); + if (EFI_ERROR(Status)) { + Print(L"Fmp->GetImage - %r\n", Status); + return ; + } + + Status = WriteFileFromBuffer(ImageName, ImageSize, Image); + Print(L"CapsuleApp: Dump %g ImageIndex (0x%x) to %s %r\n", ImageTypeId, ImageIndex, ImageName, Status); + + FreePool (Image); + + return ; +} diff --git a/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c new file mode 100644 index 000000000..dba50b320 --- /dev/null +++ b/roms/edk2/MdeModulePkg/Application/CapsuleApp/CapsuleOnDisk.c @@ -0,0 +1,842 @@ +/** @file + Process Capsule On Disk. + + Copyright (c) 2019, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CapsuleApp.h" + +EFI_GUID mCapsuleOnDiskBootOptionGuid = { 0x4CC29BB7, 0x2413, 0x40A2, { 0xB0, 0x6D, 0x25, 0x3E, 0x37, 0x10, 0xF5, 0x32 } }; + +/** + Get file name from file path. + + @param FilePath 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 Partition. + + @param DevicePath The ESP device path. + + @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 Partition. + +**/ +VOID +DumpAllEfiSysPartition ( + VOID + ) +{ + EFI_HANDLE *SimpleFileSystemHandles; + UINTN NumberSimpleFileSystemHandles; + UINTN Index; + EFI_DEVICE_PATH_PROTOCOL *DevicePath; + UINTN NumberEfiSystemPartitions; + EFI_SHELL_PROTOCOL *ShellProtocol; + + NumberEfiSystemPartitions = 0; + + ShellProtocol = GetShellProtocol (); + if (ShellProtocol == NULL) { + Print (L"Get Shell Protocol Fail\n");; + return ; + } + + 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, (VOID **)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] 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 + @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, (VOID **)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 +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 = EfiBootManagerGetNextLoadOptionDevicePath (DevicePath, CurFullPath); + + if (PreFullPath != NULL) { + FreePool (PreFullPath); + } + + if (CurFullPath == NULL) { + // + // No Active EFI system partition is found in BootOption device path + // + Status = EFI_NOT_FOUND; + break; + } + + DEBUG_CODE ( + 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] Fs The file system within EfiSysPartition + @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 +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; + BootOptionBuffer = NULL; + + ShellProtocol = GetShellProtocol (); + if (ShellProtocol == NULL) { + Print (L"Get Shell Protocol Fail\n");; + return EFI_NOT_FOUND; + } + + // + // 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, + (VOID **)&BootNextData, + NULL + ); + if (EFI_ERROR (Status) || BootNextData == NULL) { + Print (L"Get Boot Next Data Fail. Status = %r\n", Status); + return EFI_NOT_FOUND; + } else { + 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 ( (BootOptionBuffer == NULL) || + (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 buffer array + @param[in] BufferSize The buffer size array + @param[in] FileName The file name array + @param[in] BufferNum The 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; + EFI_FILE *FileHandle; + EFI_FILE_PROTOCOL *DirHandle; + UINT64 FileInfo; + VOID *Filebuffer; + UINTN FileSize; + UINTN Index; + + DirHandle = NULL; + FileHandle = NULL; + Index = 0; + + // + // 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, L"\\EFI", EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE, 0); + if (EFI_ERROR (Status)) { + Status = Root->Open (Root, &DirHandle, L"\\EFI", 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", L"\\EFI"); + return EFI_NOT_FOUND; + } + } + Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FILE_DIRECTORY, EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE , 0); + if (EFI_ERROR (Status)) { + Status = Root->Open (Root, &DirHandle, EFI_CAPSULE_FILE_DIRECTORY, 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", EFI_CAPSULE_FILE_DIRECTORY); + 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; +} + +/** + Check if Capsule On Disk is supported. + + @retval TRUE Capsule On Disk is supported. + @retval FALSE Capsule On Disk is not supported. + +**/ +BOOLEAN +IsCapsuleOnDiskSupported ( + VOID + ) +{ + EFI_STATUS Status; + UINT64 OsIndicationsSupported; + UINTN DataSize; + + DataSize = sizeof(UINT64); + Status = gRT->GetVariable ( + L"OsIndicationsSupported", + &gEfiGlobalVariableGuid, + NULL, + &DataSize, + &OsIndicationsSupported + ); + if (EFI_ERROR (Status)) { + return FALSE; + } + + if ((OsIndicationsSupported & EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED) != 0) { + return TRUE; + } + + return FALSE; +} + +/** + Process Capsule On Disk. + + @param[in] CapsuleBuffer An array of pointer to capsule images + @param[in] CapsuleBufferSize An array of UINTN to capsule images size + @param[in] FilePath An array of capsule images file path + @param[in] Map File system mapping string + @param[in] CapsuleNum The count of capsule images + + @retval EFI_SUCCESS Capsule on disk success. + @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; + CHAR16 *FileName[MAX_CAPSULE_NUM]; + UINTN Index; + + // + // Check if Capsule On Disk is supported + // + if (!IsCapsuleOnDiskSupported ()) { + Print (L"CapsuleApp: Capsule On Disk is not supported.\n"); + return EFI_UNSUPPORTED; + } + + // + // 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 devices. Status = %r\n", Status); + return Status; + } + + // + // Get file name from file path + // + for (Index = 0; Index < CapsuleNum; Index ++) { + FileName[Index] = GetFileNameFromPath (FilePath[Index]); + } + + // + // Copy capsule image to '\efi\UpdateCapsule\' + // + Status = WriteUpdateFile (CapsuleBuffer, CapsuleBufferSize, FileName, 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; +} -- cgit