| /** @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; | |
| } |