| 1 | /** @file\r |
| 2 | A shell application that triggers capsule update process.\r |
| 3 | \r |
| 4 | Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>\r |
| 5 | This program and the accompanying materials\r |
| 6 | are licensed and made available under the terms and conditions of the BSD License\r |
| 7 | which accompanies this distribution. The full text of the license may be found at\r |
| 8 | http://opensource.org/licenses/bsd-license.php\r |
| 9 | \r |
| 10 | THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r |
| 11 | WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r |
| 12 | \r |
| 13 | **/\r |
| 14 | \r |
| 15 | #include <Uefi.h>\r |
| 16 | #include <Library/BaseLib.h>\r |
| 17 | #include <Library/DebugLib.h>\r |
| 18 | #include <Library/BaseMemoryLib.h>\r |
| 19 | #include <Library/MemoryAllocationLib.h>\r |
| 20 | #include <Library/UefiBootServicesTableLib.h>\r |
| 21 | #include <Library/UefiRuntimeServicesTableLib.h>\r |
| 22 | #include <Library/UefiLib.h>\r |
| 23 | #include <Library/PrintLib.h>\r |
| 24 | #include <Protocol/LoadedImage.h>\r |
| 25 | #include <Protocol/SimpleFileSystem.h>\r |
| 26 | #include <Protocol/GraphicsOutput.h>\r |
| 27 | #include <Guid/FileInfo.h>\r |
| 28 | #include <Guid/Gpt.h>\r |
| 29 | #include <Guid/GlobalVariable.h>\r |
| 30 | #include <Guid/CapsuleReport.h>\r |
| 31 | #include <Guid/SystemResourceTable.h>\r |
| 32 | #include <Guid/FmpCapsule.h>\r |
| 33 | #include <IndustryStandard/WindowsUxCapsule.h>\r |
| 34 | \r |
| 35 | #define CAPSULE_HEADER_SIZE 0x20\r |
| 36 | \r |
| 37 | #define NESTED_CAPSULE_HEADER_SIZE SIZE_4KB\r |
| 38 | #define SYSTEM_FIRMWARE_FLAG 0x50000\r |
| 39 | #define DEVICE_FIRMWARE_FLAG 0x78010\r |
| 40 | \r |
| 41 | #define MAJOR_VERSION 1\r |
| 42 | #define MINOR_VERSION 0\r |
| 43 | \r |
| 44 | #define MAX_CAPSULE_NUM 10\r |
| 45 | \r |
| 46 | extern UINTN Argc;\r |
| 47 | extern CHAR16 **Argv;\r |
| 48 | \r |
| 49 | //\r |
| 50 | // Define how many block descriptors we want to test with.\r |
| 51 | //\r |
| 52 | UINTN NumberOfDescriptors = 1;\r |
| 53 | UINTN CapsuleFirstIndex;\r |
| 54 | UINTN CapsuleLastIndex;\r |
| 55 | \r |
| 56 | /**\r |
| 57 | Dump capsule information\r |
| 58 | \r |
| 59 | @param[in] CapsuleName The name of the capsule image.\r |
| 60 | \r |
| 61 | @retval EFI_SUCCESS The capsule information is dumped.\r |
| 62 | @retval EFI_UNSUPPORTED Input parameter is not valid.\r |
| 63 | **/\r |
| 64 | EFI_STATUS\r |
| 65 | DumpCapsule (\r |
| 66 | IN CHAR16 *CapsuleName\r |
| 67 | );\r |
| 68 | \r |
| 69 | /**\r |
| 70 | Dump capsule status variable.\r |
| 71 | \r |
| 72 | @retval EFI_SUCCESS The capsule status variable is dumped.\r |
| 73 | @retval EFI_UNSUPPORTED Input parameter is not valid.\r |
| 74 | **/\r |
| 75 | EFI_STATUS\r |
| 76 | DmpCapsuleStatusVariable (\r |
| 77 | VOID\r |
| 78 | );\r |
| 79 | \r |
| 80 | /**\r |
| 81 | Dump FMP protocol info.\r |
| 82 | **/\r |
| 83 | VOID\r |
| 84 | DumpFmpData (\r |
| 85 | VOID\r |
| 86 | );\r |
| 87 | \r |
| 88 | /**\r |
| 89 | Dump FMP image data.\r |
| 90 | \r |
| 91 | @param[in] ImageTypeId The ImageTypeId of the FMP image.\r |
| 92 | It is used to identify the FMP protocol.\r |
| 93 | @param[in] ImageIndex The ImageIndex of the FMP image.\r |
| 94 | It is the input parameter for FMP->GetImage().\r |
| 95 | @param[in] ImageName The file name to hold the output FMP image.\r |
| 96 | **/\r |
| 97 | VOID\r |
| 98 | DumpFmpImage (\r |
| 99 | IN EFI_GUID *ImageTypeId,\r |
| 100 | IN UINTN ImageIndex,\r |
| 101 | IN CHAR16 *ImageName\r |
| 102 | );\r |
| 103 | \r |
| 104 | /**\r |
| 105 | Dump ESRT info.\r |
| 106 | **/\r |
| 107 | VOID\r |
| 108 | DumpEsrtData (\r |
| 109 | VOID\r |
| 110 | );\r |
| 111 | \r |
| 112 | /**\r |
| 113 | Read a file.\r |
| 114 | \r |
| 115 | @param[in] FileName The file to be read.\r |
| 116 | @param[out] BufferSize The file buffer size\r |
| 117 | @param[out] Buffer The file buffer\r |
| 118 | \r |
| 119 | @retval EFI_SUCCESS Read file successfully\r |
| 120 | @retval EFI_NOT_FOUND File not found\r |
| 121 | **/\r |
| 122 | EFI_STATUS\r |
| 123 | ReadFileToBuffer (\r |
| 124 | IN CHAR16 *FileName,\r |
| 125 | OUT UINTN *BufferSize,\r |
| 126 | OUT VOID **Buffer\r |
| 127 | );\r |
| 128 | \r |
| 129 | /**\r |
| 130 | Write a file.\r |
| 131 | \r |
| 132 | @param[in] FileName The file to be written.\r |
| 133 | @param[in] BufferSize The file buffer size\r |
| 134 | @param[in] Buffer The file buffer\r |
| 135 | \r |
| 136 | @retval EFI_SUCCESS Write file successfully\r |
| 137 | **/\r |
| 138 | EFI_STATUS\r |
| 139 | WriteFileFromBuffer (\r |
| 140 | IN CHAR16 *FileName,\r |
| 141 | IN UINTN BufferSize,\r |
| 142 | IN VOID *Buffer\r |
| 143 | );\r |
| 144 | \r |
| 145 | /**\r |
| 146 | \r |
| 147 | This function parse application ARG.\r |
| 148 | \r |
| 149 | @return Status\r |
| 150 | **/\r |
| 151 | EFI_STATUS\r |
| 152 | GetArg (\r |
| 153 | VOID\r |
| 154 | );\r |
| 155 | \r |
| 156 | /**\r |
| 157 | Create UX capsule.\r |
| 158 | \r |
| 159 | @retval EFI_SUCCESS The capsule header is appended.\r |
| 160 | @retval EFI_UNSUPPORTED Input parameter is not valid.\r |
| 161 | @retval EFI_OUT_OF_RESOURCES No enough resource to create UX capsule.\r |
| 162 | **/\r |
| 163 | EFI_STATUS\r |
| 164 | CreateBmpFmp (\r |
| 165 | VOID\r |
| 166 | )\r |
| 167 | {\r |
| 168 | CHAR16 *OutputCapsuleName;\r |
| 169 | VOID *BmpBuffer;\r |
| 170 | UINTN FileSize;\r |
| 171 | CHAR16 *BmpName;\r |
| 172 | UINT8 *FullCapsuleBuffer;\r |
| 173 | UINTN FullCapsuleBufferSize;\r |
| 174 | EFI_DISPLAY_CAPSULE *DisplayCapsule;\r |
| 175 | EFI_STATUS Status;\r |
| 176 | EFI_GRAPHICS_OUTPUT_PROTOCOL *Gop;\r |
| 177 | \r |
| 178 | Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&Gop);\r |
| 179 | if (EFI_ERROR(Status)) {\r |
| 180 | Print(L"CapsuleApp: NO GOP is found.\n");\r |
| 181 | return EFI_UNSUPPORTED;\r |
| 182 | }\r |
| 183 | Print(L"Current GOP: Mode - %d, ", Gop->Mode->Mode);\r |
| 184 | Print(L"HorizontalResolution - %d, ", Gop->Mode->Info->HorizontalResolution);\r |
| 185 | Print(L"VerticalResolution - %d\n", Gop->Mode->Info->VerticalResolution);\r |
| 186 | // HorizontalResolution >= BMP_IMAGE_HEADER.PixelWidth\r |
| 187 | // VerticalResolution >= BMP_IMAGE_HEADER.PixelHeight\r |
| 188 | \r |
| 189 | if (Argc != 5) {\r |
| 190 | Print(L"CapsuleApp: Invalid Parameter.\n");\r |
| 191 | return EFI_UNSUPPORTED;\r |
| 192 | }\r |
| 193 | \r |
| 194 | if (StrCmp(Argv[3], L"-O") != 0) {\r |
| 195 | Print(L"CapsuleApp: NO output capsule name.\n");\r |
| 196 | return EFI_UNSUPPORTED;\r |
| 197 | }\r |
| 198 | OutputCapsuleName = Argv[4];\r |
| 199 | \r |
| 200 | BmpBuffer = NULL;\r |
| 201 | FileSize = 0;\r |
| 202 | FullCapsuleBuffer = NULL;\r |
| 203 | \r |
| 204 | BmpName = Argv[2];\r |
| 205 | Status = ReadFileToBuffer(BmpName, &FileSize, &BmpBuffer);\r |
| 206 | if (EFI_ERROR(Status)) {\r |
| 207 | Print(L"CapsuleApp: BMP image (%s) is not found.\n", BmpName);\r |
| 208 | goto Done;\r |
| 209 | }\r |
| 210 | \r |
| 211 | FullCapsuleBufferSize = sizeof(EFI_DISPLAY_CAPSULE) + FileSize;\r |
| 212 | FullCapsuleBuffer = AllocatePool(FullCapsuleBufferSize);\r |
| 213 | if (FullCapsuleBuffer == NULL) {\r |
| 214 | Print(L"CapsuleApp: Capsule Buffer size (0x%x) too big.\n", FullCapsuleBufferSize);\r |
| 215 | Status = EFI_OUT_OF_RESOURCES;\r |
| 216 | goto Done;\r |
| 217 | }\r |
| 218 | \r |
| 219 | DisplayCapsule = (EFI_DISPLAY_CAPSULE *)FullCapsuleBuffer;\r |
| 220 | CopyGuid(&DisplayCapsule->CapsuleHeader.CapsuleGuid, &gWindowsUxCapsuleGuid);\r |
| 221 | DisplayCapsule->CapsuleHeader.HeaderSize = sizeof(DisplayCapsule->CapsuleHeader);\r |
| 222 | DisplayCapsule->CapsuleHeader.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;\r |
| 223 | DisplayCapsule->CapsuleHeader.CapsuleImageSize = (UINT32)FullCapsuleBufferSize;\r |
| 224 | \r |
| 225 | DisplayCapsule->ImagePayload.Version = 1;\r |
| 226 | DisplayCapsule->ImagePayload.Checksum = 0;\r |
| 227 | DisplayCapsule->ImagePayload.ImageType = 0; // BMP\r |
| 228 | DisplayCapsule->ImagePayload.Reserved = 0;\r |
| 229 | DisplayCapsule->ImagePayload.Mode = Gop->Mode->Mode;\r |
| 230 | DisplayCapsule->ImagePayload.OffsetX = 0;\r |
| 231 | DisplayCapsule->ImagePayload.OffsetY = 0;\r |
| 232 | \r |
| 233 | CopyMem((DisplayCapsule + 1), BmpBuffer, FileSize);\r |
| 234 | \r |
| 235 | DisplayCapsule->ImagePayload.Checksum = CalculateCheckSum8(FullCapsuleBuffer, FullCapsuleBufferSize);\r |
| 236 | \r |
| 237 | Status = WriteFileFromBuffer(OutputCapsuleName, FullCapsuleBufferSize, FullCapsuleBuffer);\r |
| 238 | Print(L"CapsuleApp: Write %s %r\n", OutputCapsuleName, Status);\r |
| 239 | \r |
| 240 | Done:\r |
| 241 | if (BmpBuffer != NULL) {\r |
| 242 | FreePool(BmpBuffer);\r |
| 243 | }\r |
| 244 | \r |
| 245 | if (FullCapsuleBuffer != NULL) {\r |
| 246 | FreePool(FullCapsuleBuffer);\r |
| 247 | }\r |
| 248 | \r |
| 249 | return Status;\r |
| 250 | }\r |
| 251 | \r |
| 252 | /**\r |
| 253 | Get ImageTypeId in the FMP capsule header.\r |
| 254 | \r |
| 255 | @param[in] CapsuleHeader The FMP capsule image header.\r |
| 256 | \r |
| 257 | @return ImageTypeId\r |
| 258 | **/\r |
| 259 | EFI_GUID *\r |
| 260 | GetCapsuleImageTypeId (\r |
| 261 | IN EFI_CAPSULE_HEADER *CapsuleHeader\r |
| 262 | )\r |
| 263 | {\r |
| 264 | EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r |
| 265 | UINT64 *ItemOffsetList;\r |
| 266 | EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r |
| 267 | \r |
| 268 | FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize);\r |
| 269 | ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r |
| 270 | if (FmpCapsuleHeader->PayloadItemCount == 0) {\r |
| 271 | return NULL;\r |
| 272 | }\r |
| 273 | ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[FmpCapsuleHeader->EmbeddedDriverCount]);\r |
| 274 | return &ImageHeader->UpdateImageTypeId;\r |
| 275 | }\r |
| 276 | \r |
| 277 | /**\r |
| 278 | Get ESRT FwType according to ImageTypeId\r |
| 279 | \r |
| 280 | @param[in] ImageTypeId ImageTypeId of an FMP capsule.\r |
| 281 | \r |
| 282 | @return ESRT FwType\r |
| 283 | **/\r |
| 284 | UINT32\r |
| 285 | GetEsrtFwType (\r |
| 286 | IN EFI_GUID *ImageTypeId\r |
| 287 | )\r |
| 288 | {\r |
| 289 | EFI_STATUS Status;\r |
| 290 | EFI_SYSTEM_RESOURCE_TABLE *Esrt;\r |
| 291 | EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry;\r |
| 292 | UINTN Index;\r |
| 293 | \r |
| 294 | //\r |
| 295 | // Check ESRT\r |
| 296 | //\r |
| 297 | Status = EfiGetSystemConfigurationTable(&gEfiSystemResourceTableGuid, (VOID **)&Esrt);\r |
| 298 | if (!EFI_ERROR(Status)) {\r |
| 299 | ASSERT(Esrt != NULL);\r |
| 300 | EsrtEntry = (VOID *)(Esrt + 1);\r |
| 301 | for (Index = 0; Index < Esrt->FwResourceCount; Index++, EsrtEntry++) {\r |
| 302 | if (CompareGuid(&EsrtEntry->FwClass, ImageTypeId)) {\r |
| 303 | return EsrtEntry->FwType;\r |
| 304 | }\r |
| 305 | }\r |
| 306 | }\r |
| 307 | \r |
| 308 | return ESRT_FW_TYPE_UNKNOWN;\r |
| 309 | }\r |
| 310 | \r |
| 311 | /**\r |
| 312 | Append a capsule header on top of current image.\r |
| 313 | This function follows Windows UEFI Firmware Update Platform document.\r |
| 314 | \r |
| 315 | @retval EFI_SUCCESS The capsule header is appended.\r |
| 316 | @retval EFI_UNSUPPORTED Input parameter is not valid.\r |
| 317 | @retval EFI_OUT_OF_RESOURCES No enough resource to append capsule header.\r |
| 318 | **/\r |
| 319 | EFI_STATUS\r |
| 320 | CreateNestedFmp (\r |
| 321 | VOID\r |
| 322 | )\r |
| 323 | {\r |
| 324 | CHAR16 *OutputCapsuleName;\r |
| 325 | VOID *CapsuleBuffer;\r |
| 326 | UINTN FileSize;\r |
| 327 | CHAR16 *CapsuleName;\r |
| 328 | UINT8 *FullCapsuleBuffer;\r |
| 329 | UINTN FullCapsuleBufferSize;\r |
| 330 | EFI_CAPSULE_HEADER *NestedCapsuleHeader;\r |
| 331 | EFI_GUID *ImageTypeId;\r |
| 332 | UINT32 FwType;\r |
| 333 | EFI_STATUS Status;\r |
| 334 | \r |
| 335 | if (Argc != 5) {\r |
| 336 | Print(L"CapsuleApp: Invalid Parameter.\n");\r |
| 337 | return EFI_UNSUPPORTED;\r |
| 338 | }\r |
| 339 | \r |
| 340 | if (StrCmp(Argv[3], L"-O") != 0) {\r |
| 341 | Print(L"CapsuleApp: NO output capsule name.\n");\r |
| 342 | return EFI_UNSUPPORTED;\r |
| 343 | }\r |
| 344 | OutputCapsuleName = Argv[4];\r |
| 345 | \r |
| 346 | CapsuleBuffer = NULL;\r |
| 347 | FileSize = 0;\r |
| 348 | FullCapsuleBuffer = NULL;\r |
| 349 | \r |
| 350 | CapsuleName = Argv[2];\r |
| 351 | Status = ReadFileToBuffer(CapsuleName, &FileSize, &CapsuleBuffer);\r |
| 352 | if (EFI_ERROR(Status)) {\r |
| 353 | Print(L"CapsuleApp: Capsule image (%s) is not found.\n", CapsuleName);\r |
| 354 | goto Done;\r |
| 355 | }\r |
| 356 | \r |
| 357 | ImageTypeId = GetCapsuleImageTypeId(CapsuleBuffer);\r |
| 358 | if (ImageTypeId == NULL) {\r |
| 359 | Print(L"CapsuleApp: Capsule ImageTypeId is not found.\n");\r |
| 360 | goto Done;\r |
| 361 | }\r |
| 362 | FwType = GetEsrtFwType(ImageTypeId);\r |
| 363 | if ((FwType != ESRT_FW_TYPE_SYSTEMFIRMWARE) && (FwType != ESRT_FW_TYPE_DEVICEFIRMWARE)) {\r |
| 364 | Print(L"CapsuleApp: Capsule FwType is invalid.\n");\r |
| 365 | goto Done;\r |
| 366 | }\r |
| 367 | \r |
| 368 | FullCapsuleBufferSize = NESTED_CAPSULE_HEADER_SIZE + FileSize;\r |
| 369 | FullCapsuleBuffer = AllocatePool(FullCapsuleBufferSize);\r |
| 370 | if (FullCapsuleBuffer == NULL) {\r |
| 371 | Print(L"CapsuleApp: Capsule Buffer size (0x%x) too big.\n", FullCapsuleBufferSize);\r |
| 372 | Status = EFI_OUT_OF_RESOURCES;\r |
| 373 | goto Done;\r |
| 374 | }\r |
| 375 | \r |
| 376 | NestedCapsuleHeader = (EFI_CAPSULE_HEADER *)FullCapsuleBuffer;\r |
| 377 | ZeroMem(NestedCapsuleHeader, NESTED_CAPSULE_HEADER_SIZE);\r |
| 378 | CopyGuid(&NestedCapsuleHeader->CapsuleGuid, ImageTypeId);\r |
| 379 | NestedCapsuleHeader->HeaderSize = NESTED_CAPSULE_HEADER_SIZE;\r |
| 380 | NestedCapsuleHeader->Flags = (FwType == ESRT_FW_TYPE_DEVICEFIRMWARE) ? SYSTEM_FIRMWARE_FLAG : DEVICE_FIRMWARE_FLAG;\r |
| 381 | NestedCapsuleHeader->CapsuleImageSize = (UINT32)FullCapsuleBufferSize;\r |
| 382 | \r |
| 383 | CopyMem((UINT8 *)NestedCapsuleHeader + NestedCapsuleHeader->HeaderSize, CapsuleBuffer, FileSize);\r |
| 384 | \r |
| 385 | Status = WriteFileFromBuffer(OutputCapsuleName, FullCapsuleBufferSize, FullCapsuleBuffer);\r |
| 386 | Print(L"CapsuleApp: Write %s %r\n", OutputCapsuleName, Status);\r |
| 387 | \r |
| 388 | Done:\r |
| 389 | if (CapsuleBuffer != NULL) {\r |
| 390 | FreePool(CapsuleBuffer);\r |
| 391 | }\r |
| 392 | \r |
| 393 | if (FullCapsuleBuffer != NULL) {\r |
| 394 | FreePool(FullCapsuleBuffer);\r |
| 395 | }\r |
| 396 | \r |
| 397 | return Status;\r |
| 398 | }\r |
| 399 | \r |
| 400 | \r |
| 401 | /**\r |
| 402 | Clear capsule status variable.\r |
| 403 | \r |
| 404 | @retval EFI_SUCCESS The capsule status variable is cleared.\r |
| 405 | **/\r |
| 406 | EFI_STATUS\r |
| 407 | ClearCapsuleStatusVariable (\r |
| 408 | VOID\r |
| 409 | )\r |
| 410 | {\r |
| 411 | EFI_STATUS Status;\r |
| 412 | UINT32 Index;\r |
| 413 | CHAR16 CapsuleVarName[20];\r |
| 414 | CHAR16 *TempVarName;\r |
| 415 | \r |
| 416 | StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), L"Capsule");\r |
| 417 | TempVarName = CapsuleVarName + StrLen (CapsuleVarName);\r |
| 418 | Index = 0;\r |
| 419 | \r |
| 420 | while (TRUE) {\r |
| 421 | UnicodeSPrint (TempVarName, 5 * sizeof(CHAR16), L"%04x", Index);\r |
| 422 | \r |
| 423 | Status = gRT->SetVariable (\r |
| 424 | CapsuleVarName,\r |
| 425 | &gEfiCapsuleReportGuid,\r |
| 426 | EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r |
| 427 | 0,\r |
| 428 | (VOID *)NULL\r |
| 429 | );\r |
| 430 | if (EFI_ERROR(Status)) {\r |
| 431 | //\r |
| 432 | // There is no capsule variables, quit\r |
| 433 | //\r |
| 434 | break;\r |
| 435 | }\r |
| 436 | \r |
| 437 | Index++;\r |
| 438 | if (Index > 0xFFFF) {\r |
| 439 | break;\r |
| 440 | }\r |
| 441 | }\r |
| 442 | \r |
| 443 | return EFI_SUCCESS;\r |
| 444 | }\r |
| 445 | \r |
| 446 | /**\r |
| 447 | Build Gather list for a list of capsule images.\r |
| 448 | \r |
| 449 | @param[in] CapsuleBuffer An array of pointer to capsule images\r |
| 450 | @param[in] FileSize An array of UINTN to capsule images size\r |
| 451 | @param[in] CapsuleNum The count of capsule images\r |
| 452 | @param[out] BlockDescriptors The block descriptors for the capsule images\r |
| 453 | \r |
| 454 | @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.\r |
| 455 | **/\r |
| 456 | EFI_STATUS\r |
| 457 | BuildGatherList (\r |
| 458 | IN VOID **CapsuleBuffer,\r |
| 459 | IN UINTN *FileSize,\r |
| 460 | IN UINTN CapsuleNum,\r |
| 461 | OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors\r |
| 462 | )\r |
| 463 | {\r |
| 464 | EFI_STATUS Status;\r |
| 465 | EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors1;\r |
| 466 | EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors2;\r |
| 467 | EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorPre;\r |
| 468 | EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorsHeader;\r |
| 469 | EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr;\r |
| 470 | UINT8 *TempDataPtr;\r |
| 471 | UINTN SizeLeft;\r |
| 472 | UINTN Size;\r |
| 473 | INT32 Count;\r |
| 474 | INT32 Number;\r |
| 475 | UINTN Index;\r |
| 476 | \r |
| 477 | TempBlockPtr = NULL;\r |
| 478 | BlockDescriptors1 = NULL;\r |
| 479 | BlockDescriptors2 = NULL;\r |
| 480 | BlockDescriptorPre = NULL;\r |
| 481 | BlockDescriptorsHeader = NULL;\r |
| 482 | \r |
| 483 | for (Index = 0; Index < CapsuleNum; Index++) {\r |
| 484 | //\r |
| 485 | // Allocate memory for the descriptors.\r |
| 486 | //\r |
| 487 | if (NumberOfDescriptors == 1) {\r |
| 488 | Count = 2;\r |
| 489 | } else {\r |
| 490 | Count = (INT32)(NumberOfDescriptors + 2) / 2;\r |
| 491 | }\r |
| 492 | \r |
| 493 | Size = Count * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r |
| 494 | BlockDescriptors1 = AllocateRuntimeZeroPool (Size);\r |
| 495 | if (BlockDescriptors1 == NULL) {\r |
| 496 | Print (L"CapsuleApp: failed to allocate memory for descriptors\n");\r |
| 497 | Status = EFI_OUT_OF_RESOURCES;\r |
| 498 | goto ERREXIT;\r |
| 499 | } else {\r |
| 500 | Print (L"CapsuleApp: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1);\r |
| 501 | Print (L"CapsuleApp: capsule data starts at 0x%X with size 0x%X\n", (UINTN) CapsuleBuffer, FileSize);\r |
| 502 | }\r |
| 503 | \r |
| 504 | //\r |
| 505 | // Record descirptor header\r |
| 506 | //\r |
| 507 | if (Index == 0) {\r |
| 508 | BlockDescriptorsHeader = BlockDescriptors1;\r |
| 509 | }\r |
| 510 | \r |
| 511 | if (BlockDescriptorPre != NULL) {\r |
| 512 | BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1;\r |
| 513 | BlockDescriptorPre->Length = 0;\r |
| 514 | }\r |
| 515 | \r |
| 516 | //\r |
| 517 | // Fill them in\r |
| 518 | //\r |
| 519 | TempBlockPtr = BlockDescriptors1;\r |
| 520 | TempDataPtr = CapsuleBuffer[Index];\r |
| 521 | SizeLeft = FileSize[Index];\r |
| 522 | for (Number = 0; (Number < Count - 1) && (SizeLeft != 0); Number++) {\r |
| 523 | //\r |
| 524 | // Divide remaining data in half\r |
| 525 | //\r |
| 526 | if (NumberOfDescriptors != 1) {\r |
| 527 | if (SizeLeft == 1) {\r |
| 528 | Size = 1;\r |
| 529 | } else {\r |
| 530 | Size = SizeLeft / 2;\r |
| 531 | }\r |
| 532 | } else {\r |
| 533 | Size = SizeLeft;\r |
| 534 | }\r |
| 535 | TempBlockPtr->Union.DataBlock = (UINTN)TempDataPtr;\r |
| 536 | TempBlockPtr->Length = Size;\r |
| 537 | Print (L"CapsuleApp: capsule block/size 0x%X/0x%X\n", (UINTN) TempDataPtr, Size);\r |
| 538 | SizeLeft -= Size;\r |
| 539 | TempDataPtr += Size;\r |
| 540 | TempBlockPtr++;\r |
| 541 | }\r |
| 542 | \r |
| 543 | //\r |
| 544 | // Allocate the second list, point the first block's last entry to point\r |
| 545 | // to this one, and fill this one in. Worst case is that the previous\r |
| 546 | // list only had one element that pointed here, so we need at least two\r |
| 547 | // elements -- one to point to all the data, another to terminate the list.\r |
| 548 | //\r |
| 549 | if ((NumberOfDescriptors != 1) && (SizeLeft != 0)) {\r |
| 550 | Count = (INT32)(NumberOfDescriptors + 2) - Count;\r |
| 551 | if (Count == 1) {\r |
| 552 | Count++;\r |
| 553 | }\r |
| 554 | \r |
| 555 | Size = Count * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r |
| 556 | BlockDescriptors2 = AllocateRuntimeZeroPool (Size);\r |
| 557 | if (BlockDescriptors2 == NULL) {\r |
| 558 | Print (L"CapsuleApp: failed to allocate memory for descriptors\n");\r |
| 559 | Status = EFI_OUT_OF_RESOURCES;\r |
| 560 | goto ERREXIT;\r |
| 561 | }\r |
| 562 | \r |
| 563 | //\r |
| 564 | // Point the first list's last element to point to this second list.\r |
| 565 | //\r |
| 566 | TempBlockPtr->Union.ContinuationPointer = (UINTN) BlockDescriptors2;\r |
| 567 | \r |
| 568 | TempBlockPtr->Length = 0;\r |
| 569 | TempBlockPtr = BlockDescriptors2;\r |
| 570 | for (Number = 0; Number < Count - 1; Number++) {\r |
| 571 | //\r |
| 572 | // If second-to-last one, then dump rest to this element\r |
| 573 | //\r |
| 574 | if (Number == (Count - 2)) {\r |
| 575 | Size = SizeLeft;\r |
| 576 | } else {\r |
| 577 | //\r |
| 578 | // Divide remaining data in half\r |
| 579 | //\r |
| 580 | if (SizeLeft == 1) {\r |
| 581 | Size = 1;\r |
| 582 | } else {\r |
| 583 | Size = SizeLeft / 2;\r |
| 584 | }\r |
| 585 | }\r |
| 586 | \r |
| 587 | TempBlockPtr->Union.DataBlock = (UINTN)TempDataPtr;\r |
| 588 | TempBlockPtr->Length = Size;\r |
| 589 | Print (L"CapsuleApp: capsule block/size 0x%X/0x%X\n", (UINTN) TempDataPtr, Size);\r |
| 590 | SizeLeft -= Size;\r |
| 591 | TempDataPtr += Size;\r |
| 592 | TempBlockPtr++;\r |
| 593 | if (SizeLeft == 0) {\r |
| 594 | break;\r |
| 595 | }\r |
| 596 | }\r |
| 597 | }\r |
| 598 | \r |
| 599 | BlockDescriptorPre = TempBlockPtr;\r |
| 600 | BlockDescriptors1 = NULL;\r |
| 601 | }\r |
| 602 | \r |
| 603 | //\r |
| 604 | // Null-terminate.\r |
| 605 | //\r |
| 606 | if (TempBlockPtr != NULL) {\r |
| 607 | TempBlockPtr->Union.ContinuationPointer = (UINTN)NULL;\r |
| 608 | TempBlockPtr->Length = 0;\r |
| 609 | *BlockDescriptors = BlockDescriptorsHeader;\r |
| 610 | }\r |
| 611 | \r |
| 612 | return EFI_SUCCESS;\r |
| 613 | \r |
| 614 | ERREXIT:\r |
| 615 | if (BlockDescriptors1 != NULL) {\r |
| 616 | FreePool(BlockDescriptors1);\r |
| 617 | }\r |
| 618 | \r |
| 619 | if (BlockDescriptors2 != NULL) {\r |
| 620 | FreePool(BlockDescriptors2);\r |
| 621 | }\r |
| 622 | \r |
| 623 | return Status;\r |
| 624 | }\r |
| 625 | \r |
| 626 | /**\r |
| 627 | Clear the Gather list for a list of capsule images.\r |
| 628 | \r |
| 629 | @param[in] BlockDescriptors The block descriptors for the capsule images\r |
| 630 | @param[in] CapsuleNum The count of capsule images\r |
| 631 | **/\r |
| 632 | VOID\r |
| 633 | CleanGatherList (\r |
| 634 | IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors,\r |
| 635 | IN UINTN CapsuleNum\r |
| 636 | )\r |
| 637 | {\r |
| 638 | EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr;\r |
| 639 | EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr1;\r |
| 640 | EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr2;\r |
| 641 | UINTN Index;\r |
| 642 | \r |
| 643 | if (BlockDescriptors != NULL) {\r |
| 644 | TempBlockPtr1 = BlockDescriptors;\r |
| 645 | while (1){\r |
| 646 | TempBlockPtr = TempBlockPtr1;\r |
| 647 | for (Index = 0; Index < CapsuleNum; Index++) {\r |
| 648 | if (TempBlockPtr[Index].Length == 0) {\r |
| 649 | break;\r |
| 650 | }\r |
| 651 | }\r |
| 652 | \r |
| 653 | if (TempBlockPtr[Index].Union.ContinuationPointer == (UINTN)NULL) {\r |
| 654 | break;\r |
| 655 | }\r |
| 656 | \r |
| 657 | TempBlockPtr2 = (VOID *) ((UINTN) TempBlockPtr->Union.ContinuationPointer);\r |
| 658 | FreePool(TempBlockPtr1);\r |
| 659 | TempBlockPtr1 = TempBlockPtr2;\r |
| 660 | }\r |
| 661 | }\r |
| 662 | }\r |
| 663 | \r |
| 664 | /**\r |
| 665 | Print APP usage.\r |
| 666 | **/\r |
| 667 | VOID\r |
| 668 | PrintUsage (\r |
| 669 | VOID\r |
| 670 | )\r |
| 671 | {\r |
| 672 | Print(L"CapsuleApp: usage\n");\r |
| 673 | Print(L" CapsuleApp <Capsule...>\n");\r |
| 674 | Print(L" CapsuleApp -S\n");\r |
| 675 | Print(L" CapsuleApp -C\n");\r |
| 676 | Print(L" CapsuleApp -P\n");\r |
| 677 | Print(L" CapsuleApp -E\n");\r |
| 678 | Print(L" CapsuleApp -G <BMP> -O <Capsule>\n");\r |
| 679 | Print(L" CapsuleApp -N <Capsule> -O <NestedCapsule>\n");\r |
| 680 | Print(L" CapsuleApp -D <Capsule>\n");\r |
| 681 | Print(L" CapsuleApp -P GET <ImageTypeId> <Index> -O <FileName>\n");\r |
| 682 | Print(L"Parameter:\n");\r |
| 683 | Print(L" -S: Dump capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");\r |
| 684 | Print(L" which is defined in UEFI specification.\n");\r |
| 685 | Print(L" -C: Clear capsule report variable (EFI_CAPSULE_RPORT_GUID),\n");\r |
| 686 | Print(L" which is defined in UEFI specification.\n");\r |
| 687 | Print(L" -P: Dump UEFI FMP protocol info.\n");\r |
| 688 | Print(L" -E: Dump UEFI ESRT table info.\n");\r |
| 689 | Print(L" -G: Convert a BMP file to be a UX capsule,\n");\r |
| 690 | Print(L" according to Windows Firmware Update document\n");\r |
| 691 | Print(L" -N: Append a Capsule Header to an existing capsule image,\n");\r |
| 692 | Print(L" according to Windows Firmware Update document\n");\r |
| 693 | Print(L" -O: Output new Capsule file name\n");\r |
| 694 | Print(L" -D: Dump Capsule image header information and FMP header information,\n");\r |
| 695 | Print(L" if it is an FMP capsule.\n");\r |
| 696 | }\r |
| 697 | \r |
| 698 | /**\r |
| 699 | Update Capsule image.\r |
| 700 | \r |
| 701 | @param[in] ImageHandle The image handle.\r |
| 702 | @param[in] SystemTable The system table.\r |
| 703 | \r |
| 704 | @retval EFI_SUCCESS Command completed successfully.\r |
| 705 | @retval EFI_INVALID_PARAMETER Command usage error.\r |
| 706 | @retval EFI_NOT_FOUND The input file can't be found.\r |
| 707 | **/\r |
| 708 | EFI_STATUS\r |
| 709 | EFIAPI\r |
| 710 | UefiMain (\r |
| 711 | IN EFI_HANDLE ImageHandle,\r |
| 712 | IN EFI_SYSTEM_TABLE *SystemTable\r |
| 713 | )\r |
| 714 | {\r |
| 715 | EFI_STATUS Status;\r |
| 716 | RETURN_STATUS RStatus;\r |
| 717 | UINTN FileSize[MAX_CAPSULE_NUM];\r |
| 718 | VOID *CapsuleBuffer[MAX_CAPSULE_NUM];\r |
| 719 | EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;\r |
| 720 | EFI_CAPSULE_HEADER *CapsuleHeaderArray[MAX_CAPSULE_NUM + 1];\r |
| 721 | UINT64 MaxCapsuleSize;\r |
| 722 | EFI_RESET_TYPE ResetType;\r |
| 723 | BOOLEAN NeedReset;\r |
| 724 | CHAR16 *CapsuleName;\r |
| 725 | UINTN CapsuleNum;\r |
| 726 | UINTN Index;\r |
| 727 | \r |
| 728 | Status = GetArg();\r |
| 729 | if (EFI_ERROR(Status)) {\r |
| 730 | Print(L"Please use UEFI SHELL to run this application!\n", Status);\r |
| 731 | return Status;\r |
| 732 | }\r |
| 733 | if (Argc < 2) {\r |
| 734 | PrintUsage();\r |
| 735 | return EFI_INVALID_PARAMETER;\r |
| 736 | }\r |
| 737 | if (StrCmp(Argv[1], L"-D") == 0) {\r |
| 738 | Status = DumpCapsule(Argv[2]);\r |
| 739 | return Status;\r |
| 740 | }\r |
| 741 | if (StrCmp(Argv[1], L"-G") == 0) {\r |
| 742 | Status = CreateBmpFmp();\r |
| 743 | return Status;\r |
| 744 | }\r |
| 745 | if (StrCmp(Argv[1], L"-N") == 0) {\r |
| 746 | Status = CreateNestedFmp();\r |
| 747 | return Status;\r |
| 748 | }\r |
| 749 | if (StrCmp(Argv[1], L"-S") == 0) {\r |
| 750 | Status = DmpCapsuleStatusVariable();\r |
| 751 | return EFI_SUCCESS;\r |
| 752 | }\r |
| 753 | if (StrCmp(Argv[1], L"-C") == 0) {\r |
| 754 | Status = ClearCapsuleStatusVariable();\r |
| 755 | return Status;\r |
| 756 | }\r |
| 757 | if (StrCmp(Argv[1], L"-P") == 0) {\r |
| 758 | if (Argc == 2) {\r |
| 759 | DumpFmpData();\r |
| 760 | }\r |
| 761 | if (Argc >= 3) {\r |
| 762 | if (StrCmp(Argv[2], L"GET") == 0) {\r |
| 763 | EFI_GUID ImageTypeId;\r |
| 764 | UINTN ImageIndex;\r |
| 765 | //\r |
| 766 | // FMP->GetImage()\r |
| 767 | //\r |
| 768 | RStatus = StrToGuid (Argv[3], &ImageTypeId);\r |
| 769 | if (RETURN_ERROR (RStatus) || (Argv[3][GUID_STRING_LENGTH] != L'\0')) {\r |
| 770 | Print (L"Invalid ImageTypeId - %s\n", Argv[3]);\r |
| 771 | return EFI_INVALID_PARAMETER;\r |
| 772 | }\r |
| 773 | ImageIndex = StrDecimalToUintn(Argv[4]);\r |
| 774 | if (StrCmp(Argv[5], L"-O") == 0) {\r |
| 775 | DumpFmpImage(&ImageTypeId, ImageIndex, Argv[6]);\r |
| 776 | }\r |
| 777 | }\r |
| 778 | }\r |
| 779 | return EFI_SUCCESS;\r |
| 780 | }\r |
| 781 | if (StrCmp(Argv[1], L"-E") == 0) {\r |
| 782 | DumpEsrtData();\r |
| 783 | return EFI_SUCCESS;\r |
| 784 | }\r |
| 785 | CapsuleFirstIndex = 1;\r |
| 786 | CapsuleLastIndex = Argc - 1;\r |
| 787 | CapsuleNum = CapsuleLastIndex - CapsuleFirstIndex + 1;\r |
| 788 | \r |
| 789 | if (CapsuleFirstIndex > CapsuleLastIndex) {\r |
| 790 | Print(L"CapsuleApp: NO capsule image.\n");\r |
| 791 | return EFI_UNSUPPORTED;\r |
| 792 | }\r |
| 793 | if (CapsuleNum > MAX_CAPSULE_NUM) {\r |
| 794 | Print(L"CapsuleApp: Too many capsule images.\n");\r |
| 795 | return EFI_UNSUPPORTED;\r |
| 796 | }\r |
| 797 | \r |
| 798 | ZeroMem(&CapsuleBuffer, sizeof(CapsuleBuffer));\r |
| 799 | ZeroMem(&FileSize, sizeof(FileSize));\r |
| 800 | BlockDescriptors = NULL;\r |
| 801 | \r |
| 802 | for (Index = 0; Index < CapsuleNum; Index++) {\r |
| 803 | CapsuleName = Argv[CapsuleFirstIndex + Index];\r |
| 804 | Status = ReadFileToBuffer(CapsuleName, &FileSize[Index], &CapsuleBuffer[Index]);\r |
| 805 | if (EFI_ERROR(Status)) {\r |
| 806 | Print(L"CapsuleApp: capsule image (%s) is not found.\n", CapsuleName);\r |
| 807 | goto Done;\r |
| 808 | }\r |
| 809 | }\r |
| 810 | \r |
| 811 | //\r |
| 812 | // Every capsule use 2 descriptor 1 for data 1 for end\r |
| 813 | //\r |
| 814 | Status = BuildGatherList(CapsuleBuffer, FileSize, CapsuleNum, &BlockDescriptors);\r |
| 815 | if (EFI_ERROR(Status)) {\r |
| 816 | goto Done;\r |
| 817 | }\r |
| 818 | \r |
| 819 | //\r |
| 820 | // Call the runtime service capsule.\r |
| 821 | //\r |
| 822 | NeedReset = FALSE;\r |
| 823 | for (Index = 0; Index < CapsuleNum; Index++) {\r |
| 824 | CapsuleHeaderArray[Index] = (EFI_CAPSULE_HEADER *) CapsuleBuffer[Index];\r |
| 825 | if ((CapsuleHeaderArray[Index]->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {\r |
| 826 | NeedReset = TRUE;\r |
| 827 | }\r |
| 828 | }\r |
| 829 | CapsuleHeaderArray[CapsuleNum] = NULL;\r |
| 830 | \r |
| 831 | //\r |
| 832 | // Inquire platform capability of UpdateCapsule.\r |
| 833 | //\r |
| 834 | Status = gRT->QueryCapsuleCapabilities (CapsuleHeaderArray, CapsuleNum, &MaxCapsuleSize, &ResetType);\r |
| 835 | if (EFI_ERROR(Status)) {\r |
| 836 | Print (L"CapsuleApp: failed to query capsule capability - %r\n", Status);\r |
| 837 | goto Done;\r |
| 838 | }\r |
| 839 | \r |
| 840 | for (Index = 0; Index < CapsuleNum; Index++) {\r |
| 841 | if (FileSize[Index] > MaxCapsuleSize) {\r |
| 842 | Print (L"CapsuleApp: capsule is too large to update, %ld is allowed\n", MaxCapsuleSize);\r |
| 843 | Status = EFI_UNSUPPORTED;\r |
| 844 | goto Done;\r |
| 845 | }\r |
| 846 | }\r |
| 847 | \r |
| 848 | //\r |
| 849 | // Check whether the input capsule image has the flag of persist across system reset.\r |
| 850 | //\r |
| 851 | if (NeedReset) {\r |
| 852 | Status = gRT->UpdateCapsule(CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors);\r |
| 853 | if (Status != EFI_SUCCESS) {\r |
| 854 | Print (L"CapsuleApp: failed to update capsule - %r\n", Status);\r |
| 855 | goto Done;\r |
| 856 | }\r |
| 857 | //\r |
| 858 | // For capsule who has reset flag, after calling UpdateCapsule service,triger a\r |
| 859 | // system reset to process capsule persist across a system reset.\r |
| 860 | //\r |
| 861 | gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL);\r |
| 862 | } else {\r |
| 863 | //\r |
| 864 | // For capsule who has no reset flag, only call UpdateCapsule Service without a\r |
| 865 | // system reset. The service will process the capsule immediately.\r |
| 866 | //\r |
| 867 | Status = gRT->UpdateCapsule (CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors);\r |
| 868 | if (Status != EFI_SUCCESS) {\r |
| 869 | Print (L"CapsuleApp: failed to update capsule - %r\n", Status);\r |
| 870 | }\r |
| 871 | }\r |
| 872 | \r |
| 873 | Status = EFI_SUCCESS;\r |
| 874 | \r |
| 875 | Done:\r |
| 876 | for (Index = 0; Index < CapsuleNum; Index++) {\r |
| 877 | if (CapsuleBuffer[Index] != NULL) {\r |
| 878 | FreePool (CapsuleBuffer[Index]);\r |
| 879 | }\r |
| 880 | }\r |
| 881 | \r |
| 882 | CleanGatherList(BlockDescriptors, CapsuleNum);\r |
| 883 | \r |
| 884 | return Status;\r |
| 885 | }\r |