diff options
Diffstat (limited to 'src/VBox/Devices/EFI/Firmware/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c')
-rw-r--r-- | src/VBox/Devices/EFI/Firmware/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c | 1015 |
1 files changed, 1015 insertions, 0 deletions
diff --git a/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c b/src/VBox/Devices/EFI/Firmware/MdeModulePkg/Application/CapsuleApp/CapsuleApp.c new file mode 100644 index 00000000..57bb3f2b --- /dev/null +++ b/src/VBox/Devices/EFI/Firmware/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.<BR> + 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 <Capsule...> [-NR] [-OD [FSx]]\n"); + Print(L" CapsuleApp -S\n"); + Print(L" CapsuleApp -C\n"); + Print(L" CapsuleApp -P\n"); + Print(L" CapsuleApp -E\n"); + Print(L" CapsuleApp -L\n"); + Print(L" CapsuleApp -L INFO\n"); + Print(L" CapsuleApp -F\n"); + Print(L" CapsuleApp -G <BMP> -O <Capsule>\n"); + Print(L" CapsuleApp -N <Capsule> -O <NestedCapsule>\n"); + Print(L" CapsuleApp -D <Capsule>\n"); + Print(L" CapsuleApp -P GET <ImageTypeId> <Index> -O <FileName>\n"); + Print(L"Parameter:\n"); + Print(L" -NR: No reset will be triggered for the capsule\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; +} |