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