]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
MdeModulePkg CapsuleApp: Check capsule header before using its Flags
[mirror_edk2.git] / MdeModulePkg / Application / CapsuleApp / CapsuleApp.c
CommitLineData
592bad04
JY
1/** @file\r
2 A shell application that triggers capsule update process.\r
3\r
845f7cfe 4 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
592bad04
JY
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
bf9b044e 24#include <Library/BmpSupportLib.h>\r
592bad04 25#include <Protocol/GraphicsOutput.h>\r
592bad04
JY
26#include <Guid/GlobalVariable.h>\r
27#include <Guid/CapsuleReport.h>\r
28#include <Guid/SystemResourceTable.h>\r
29#include <Guid/FmpCapsule.h>\r
30#include <IndustryStandard/WindowsUxCapsule.h>\r
31\r
32#define CAPSULE_HEADER_SIZE 0x20\r
33\r
34#define NESTED_CAPSULE_HEADER_SIZE SIZE_4KB\r
35#define SYSTEM_FIRMWARE_FLAG 0x50000\r
36#define DEVICE_FIRMWARE_FLAG 0x78010\r
37\r
592bad04
JY
38#define MAJOR_VERSION 1\r
39#define MINOR_VERSION 0\r
40\r
41#define MAX_CAPSULE_NUM 10\r
42\r
43extern UINTN Argc;\r
44extern CHAR16 **Argv;\r
45\r
46//\r
47// Define how many block descriptors we want to test with.\r
48//\r
49UINTN NumberOfDescriptors = 1;\r
50UINTN CapsuleFirstIndex;\r
51UINTN CapsuleLastIndex;\r
52\r
53/**\r
54 Dump capsule information\r
55\r
56 @param[in] CapsuleName The name of the capsule image.\r
57\r
58 @retval EFI_SUCCESS The capsule information is dumped.\r
59 @retval EFI_UNSUPPORTED Input parameter is not valid.\r
60**/\r
61EFI_STATUS\r
62DumpCapsule (\r
63 IN CHAR16 *CapsuleName\r
64 );\r
65\r
66/**\r
67 Dump capsule status variable.\r
68\r
69 @retval EFI_SUCCESS The capsule status variable is dumped.\r
70 @retval EFI_UNSUPPORTED Input parameter is not valid.\r
71**/\r
72EFI_STATUS\r
73DmpCapsuleStatusVariable (\r
74 VOID\r
75 );\r
76\r
77/**\r
78 Dump FMP protocol info.\r
79**/\r
80VOID\r
81DumpFmpData (\r
82 VOID\r
83 );\r
84\r
1e09ec09
JY
85/**\r
86 Dump FMP image data.\r
87\r
88 @param[in] ImageTypeId The ImageTypeId of the FMP image.\r
89 It is used to identify the FMP protocol.\r
90 @param[in] ImageIndex The ImageIndex of the FMP image.\r
91 It is the input parameter for FMP->GetImage().\r
92 @param[in] ImageName The file name to hold the output FMP image.\r
93**/\r
94VOID\r
95DumpFmpImage (\r
96 IN EFI_GUID *ImageTypeId,\r
97 IN UINTN ImageIndex,\r
98 IN CHAR16 *ImageName\r
99 );\r
100\r
592bad04
JY
101/**\r
102 Dump ESRT info.\r
103**/\r
104VOID\r
105DumpEsrtData (\r
106 VOID\r
107 );\r
108\r
109/**\r
110 Read a file.\r
111\r
112 @param[in] FileName The file to be read.\r
113 @param[out] BufferSize The file buffer size\r
114 @param[out] Buffer The file buffer\r
115\r
116 @retval EFI_SUCCESS Read file successfully\r
8b17683a
SZ
117 @retval EFI_NOT_FOUND Shell protocol or file not found\r
118 @retval others Read file failed\r
592bad04
JY
119**/\r
120EFI_STATUS\r
121ReadFileToBuffer (\r
122 IN CHAR16 *FileName,\r
123 OUT UINTN *BufferSize,\r
124 OUT VOID **Buffer\r
125 );\r
126\r
127/**\r
128 Write a file.\r
129\r
130 @param[in] FileName The file to be written.\r
131 @param[in] BufferSize The file buffer size\r
132 @param[in] Buffer The file buffer\r
133\r
134 @retval EFI_SUCCESS Write file successfully\r
8b17683a
SZ
135 @retval EFI_NOT_FOUND Shell protocol not found\r
136 @retval others Write file failed\r
592bad04
JY
137**/\r
138EFI_STATUS\r
139WriteFileFromBuffer (\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
151EFI_STATUS\r
152GetArg (\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
163EFI_STATUS\r
164CreateBmpFmp (\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
bf9b044e
MK
177 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;\r
178 EFI_GRAPHICS_OUTPUT_BLT_PIXEL *GopBlt;\r
179 UINTN GopBltSize;\r
180 UINTN Height;\r
181 UINTN Width;\r
592bad04
JY
182\r
183 Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **)&Gop);\r
184 if (EFI_ERROR(Status)) {\r
185 Print(L"CapsuleApp: NO GOP is found.\n");\r
186 return EFI_UNSUPPORTED;\r
187 }\r
bf9b044e 188 Info = Gop->Mode->Info;\r
592bad04 189 Print(L"Current GOP: Mode - %d, ", Gop->Mode->Mode);\r
bf9b044e
MK
190 Print(L"HorizontalResolution - %d, ", Info->HorizontalResolution);\r
191 Print(L"VerticalResolution - %d\n", Info->VerticalResolution);\r
592bad04
JY
192 // HorizontalResolution >= BMP_IMAGE_HEADER.PixelWidth\r
193 // VerticalResolution >= BMP_IMAGE_HEADER.PixelHeight\r
194\r
195 if (Argc != 5) {\r
196 Print(L"CapsuleApp: Invalid Parameter.\n");\r
197 return EFI_UNSUPPORTED;\r
198 }\r
199\r
200 if (StrCmp(Argv[3], L"-O") != 0) {\r
201 Print(L"CapsuleApp: NO output capsule name.\n");\r
202 return EFI_UNSUPPORTED;\r
203 }\r
204 OutputCapsuleName = Argv[4];\r
205\r
206 BmpBuffer = NULL;\r
207 FileSize = 0;\r
208 FullCapsuleBuffer = NULL;\r
209\r
210 BmpName = Argv[2];\r
211 Status = ReadFileToBuffer(BmpName, &FileSize, &BmpBuffer);\r
212 if (EFI_ERROR(Status)) {\r
213 Print(L"CapsuleApp: BMP image (%s) is not found.\n", BmpName);\r
214 goto Done;\r
215 }\r
216\r
bf9b044e
MK
217 GopBlt = NULL;\r
218 Status = TranslateBmpToGopBlt (\r
219 BmpBuffer,\r
220 FileSize,\r
221 &GopBlt,\r
222 &GopBltSize,\r
223 &Height,\r
224 &Width\r
225 );\r
226 if (EFI_ERROR(Status)) {\r
227 Print(L"CapsuleApp: BMP image (%s) is not valid.\n", BmpName);\r
228 goto Done;\r
229 }\r
230 if (GopBlt != NULL) {\r
231 FreePool (GopBlt);\r
232 }\r
233 Print(L"BMP image (%s), Width - %d, Height - %d\n", BmpName, Width, Height);\r
234\r
235 if (Height > Info->VerticalResolution) {\r
236 Status = EFI_INVALID_PARAMETER;\r
237 Print(L"CapsuleApp: BMP image (%s) height is larger than current resolution.\n", BmpName);\r
238 goto Done;\r
239 }\r
240 if (Width > Info->HorizontalResolution) {\r
241 Status = EFI_INVALID_PARAMETER;\r
242 Print(L"CapsuleApp: BMP image (%s) width is larger than current resolution.\n", BmpName);\r
243 goto Done;\r
244 }\r
245\r
592bad04
JY
246 FullCapsuleBufferSize = sizeof(EFI_DISPLAY_CAPSULE) + FileSize;\r
247 FullCapsuleBuffer = AllocatePool(FullCapsuleBufferSize);\r
248 if (FullCapsuleBuffer == NULL) {\r
249 Print(L"CapsuleApp: Capsule Buffer size (0x%x) too big.\n", FullCapsuleBufferSize);\r
250 Status = EFI_OUT_OF_RESOURCES;\r
251 goto Done;\r
252 }\r
253\r
254 DisplayCapsule = (EFI_DISPLAY_CAPSULE *)FullCapsuleBuffer;\r
255 CopyGuid(&DisplayCapsule->CapsuleHeader.CapsuleGuid, &gWindowsUxCapsuleGuid);\r
256 DisplayCapsule->CapsuleHeader.HeaderSize = sizeof(DisplayCapsule->CapsuleHeader);\r
257 DisplayCapsule->CapsuleHeader.Flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET;\r
258 DisplayCapsule->CapsuleHeader.CapsuleImageSize = (UINT32)FullCapsuleBufferSize;\r
259\r
260 DisplayCapsule->ImagePayload.Version = 1;\r
261 DisplayCapsule->ImagePayload.Checksum = 0;\r
262 DisplayCapsule->ImagePayload.ImageType = 0; // BMP\r
263 DisplayCapsule->ImagePayload.Reserved = 0;\r
264 DisplayCapsule->ImagePayload.Mode = Gop->Mode->Mode;\r
bf9b044e
MK
265\r
266 //\r
267 // Center the bitmap horizontally\r
268 //\r
269 DisplayCapsule->ImagePayload.OffsetX = (UINT32)((Info->HorizontalResolution - Width) / 2);\r
270\r
271 //\r
272 // Put bitmap 3/4 down the display. If bitmap is too tall, then align bottom\r
273 // of bitmap at bottom of display.\r
274 //\r
275 DisplayCapsule->ImagePayload.OffsetY =\r
276 MIN (\r
277 (UINT32)(Info->VerticalResolution - Height),\r
278 (UINT32)(((3 * Info->VerticalResolution) - (2 * Height)) / 4)\r
279 );\r
280\r
281 Print(L"BMP image (%s), OffsetX - %d, OffsetY - %d\n",\r
282 BmpName,\r
283 DisplayCapsule->ImagePayload.OffsetX,\r
284 DisplayCapsule->ImagePayload.OffsetY\r
285 );\r
592bad04
JY
286\r
287 CopyMem((DisplayCapsule + 1), BmpBuffer, FileSize);\r
288\r
289 DisplayCapsule->ImagePayload.Checksum = CalculateCheckSum8(FullCapsuleBuffer, FullCapsuleBufferSize);\r
290\r
291 Status = WriteFileFromBuffer(OutputCapsuleName, FullCapsuleBufferSize, FullCapsuleBuffer);\r
292 Print(L"CapsuleApp: Write %s %r\n", OutputCapsuleName, Status);\r
293\r
294Done:\r
295 if (BmpBuffer != NULL) {\r
296 FreePool(BmpBuffer);\r
297 }\r
298\r
299 if (FullCapsuleBuffer != NULL) {\r
300 FreePool(FullCapsuleBuffer);\r
301 }\r
302\r
303 return Status;\r
304}\r
305\r
306/**\r
307 Get ImageTypeId in the FMP capsule header.\r
308\r
309 @param[in] CapsuleHeader The FMP capsule image header.\r
310\r
311 @return ImageTypeId\r
312**/\r
313EFI_GUID *\r
314GetCapsuleImageTypeId (\r
315 IN EFI_CAPSULE_HEADER *CapsuleHeader\r
316 )\r
317{\r
318 EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
319 UINT64 *ItemOffsetList;\r
320 EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
321\r
322 FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize);\r
323 ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
324 if (FmpCapsuleHeader->PayloadItemCount == 0) {\r
325 return NULL;\r
326 }\r
327 ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[FmpCapsuleHeader->EmbeddedDriverCount]);\r
328 return &ImageHeader->UpdateImageTypeId;\r
329}\r
330\r
331/**\r
332 Get ESRT FwType according to ImageTypeId\r
333\r
334 @param[in] ImageTypeId ImageTypeId of an FMP capsule.\r
335\r
336 @return ESRT FwType\r
337**/\r
338UINT32\r
339GetEsrtFwType (\r
340 IN EFI_GUID *ImageTypeId\r
341 )\r
342{\r
343 EFI_STATUS Status;\r
344 EFI_SYSTEM_RESOURCE_TABLE *Esrt;\r
345 EFI_SYSTEM_RESOURCE_ENTRY *EsrtEntry;\r
346 UINTN Index;\r
347\r
348 //\r
349 // Check ESRT\r
350 //\r
351 Status = EfiGetSystemConfigurationTable(&gEfiSystemResourceTableGuid, (VOID **)&Esrt);\r
352 if (!EFI_ERROR(Status)) {\r
353 ASSERT(Esrt != NULL);\r
354 EsrtEntry = (VOID *)(Esrt + 1);\r
355 for (Index = 0; Index < Esrt->FwResourceCount; Index++, EsrtEntry++) {\r
356 if (CompareGuid(&EsrtEntry->FwClass, ImageTypeId)) {\r
357 return EsrtEntry->FwType;\r
358 }\r
359 }\r
360 }\r
361\r
362 return ESRT_FW_TYPE_UNKNOWN;\r
363}\r
364\r
365/**\r
366 Append a capsule header on top of current image.\r
367 This function follows Windows UEFI Firmware Update Platform document.\r
368\r
369 @retval EFI_SUCCESS The capsule header is appended.\r
370 @retval EFI_UNSUPPORTED Input parameter is not valid.\r
371 @retval EFI_OUT_OF_RESOURCES No enough resource to append capsule header.\r
372**/\r
373EFI_STATUS\r
374CreateNestedFmp (\r
375 VOID\r
376 )\r
377{\r
378 CHAR16 *OutputCapsuleName;\r
379 VOID *CapsuleBuffer;\r
380 UINTN FileSize;\r
381 CHAR16 *CapsuleName;\r
382 UINT8 *FullCapsuleBuffer;\r
383 UINTN FullCapsuleBufferSize;\r
384 EFI_CAPSULE_HEADER *NestedCapsuleHeader;\r
385 EFI_GUID *ImageTypeId;\r
386 UINT32 FwType;\r
387 EFI_STATUS Status;\r
388\r
389 if (Argc != 5) {\r
390 Print(L"CapsuleApp: Invalid Parameter.\n");\r
391 return EFI_UNSUPPORTED;\r
392 }\r
393\r
394 if (StrCmp(Argv[3], L"-O") != 0) {\r
395 Print(L"CapsuleApp: NO output capsule name.\n");\r
396 return EFI_UNSUPPORTED;\r
397 }\r
398 OutputCapsuleName = Argv[4];\r
399\r
400 CapsuleBuffer = NULL;\r
401 FileSize = 0;\r
402 FullCapsuleBuffer = NULL;\r
403\r
404 CapsuleName = Argv[2];\r
405 Status = ReadFileToBuffer(CapsuleName, &FileSize, &CapsuleBuffer);\r
406 if (EFI_ERROR(Status)) {\r
407 Print(L"CapsuleApp: Capsule image (%s) is not found.\n", CapsuleName);\r
408 goto Done;\r
409 }\r
410\r
411 ImageTypeId = GetCapsuleImageTypeId(CapsuleBuffer);\r
412 if (ImageTypeId == NULL) {\r
413 Print(L"CapsuleApp: Capsule ImageTypeId is not found.\n");\r
414 goto Done;\r
415 }\r
416 FwType = GetEsrtFwType(ImageTypeId);\r
417 if ((FwType != ESRT_FW_TYPE_SYSTEMFIRMWARE) && (FwType != ESRT_FW_TYPE_DEVICEFIRMWARE)) {\r
418 Print(L"CapsuleApp: Capsule FwType is invalid.\n");\r
419 goto Done;\r
420 }\r
421\r
422 FullCapsuleBufferSize = NESTED_CAPSULE_HEADER_SIZE + FileSize;\r
423 FullCapsuleBuffer = AllocatePool(FullCapsuleBufferSize);\r
424 if (FullCapsuleBuffer == NULL) {\r
425 Print(L"CapsuleApp: Capsule Buffer size (0x%x) too big.\n", FullCapsuleBufferSize);\r
426 Status = EFI_OUT_OF_RESOURCES;\r
427 goto Done;\r
428 }\r
429\r
430 NestedCapsuleHeader = (EFI_CAPSULE_HEADER *)FullCapsuleBuffer;\r
431 ZeroMem(NestedCapsuleHeader, NESTED_CAPSULE_HEADER_SIZE);\r
432 CopyGuid(&NestedCapsuleHeader->CapsuleGuid, ImageTypeId);\r
433 NestedCapsuleHeader->HeaderSize = NESTED_CAPSULE_HEADER_SIZE;\r
99fd3043 434 NestedCapsuleHeader->Flags = (FwType == ESRT_FW_TYPE_SYSTEMFIRMWARE) ? SYSTEM_FIRMWARE_FLAG : DEVICE_FIRMWARE_FLAG;\r
592bad04
JY
435 NestedCapsuleHeader->CapsuleImageSize = (UINT32)FullCapsuleBufferSize;\r
436\r
437 CopyMem((UINT8 *)NestedCapsuleHeader + NestedCapsuleHeader->HeaderSize, CapsuleBuffer, FileSize);\r
438\r
439 Status = WriteFileFromBuffer(OutputCapsuleName, FullCapsuleBufferSize, FullCapsuleBuffer);\r
440 Print(L"CapsuleApp: Write %s %r\n", OutputCapsuleName, Status);\r
441\r
442Done:\r
443 if (CapsuleBuffer != NULL) {\r
444 FreePool(CapsuleBuffer);\r
445 }\r
446\r
447 if (FullCapsuleBuffer != NULL) {\r
448 FreePool(FullCapsuleBuffer);\r
449 }\r
450\r
451 return Status;\r
452}\r
453\r
454\r
455/**\r
456 Clear capsule status variable.\r
457\r
458 @retval EFI_SUCCESS The capsule status variable is cleared.\r
459**/\r
460EFI_STATUS\r
461ClearCapsuleStatusVariable (\r
462 VOID\r
463 )\r
464{\r
465 EFI_STATUS Status;\r
466 UINT32 Index;\r
467 CHAR16 CapsuleVarName[20];\r
468 CHAR16 *TempVarName;\r
469\r
470 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), L"Capsule");\r
471 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);\r
472 Index = 0;\r
473\r
474 while (TRUE) {\r
475 UnicodeSPrint (TempVarName, 5 * sizeof(CHAR16), L"%04x", Index);\r
476\r
477 Status = gRT->SetVariable (\r
478 CapsuleVarName,\r
479 &gEfiCapsuleReportGuid,\r
480 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,\r
481 0,\r
482 (VOID *)NULL\r
483 );\r
484 if (EFI_ERROR(Status)) {\r
485 //\r
486 // There is no capsule variables, quit\r
487 //\r
488 break;\r
489 }\r
490\r
491 Index++;\r
492 if (Index > 0xFFFF) {\r
493 break;\r
494 }\r
495 }\r
496\r
497 return EFI_SUCCESS;\r
498}\r
499\r
500/**\r
501 Build Gather list for a list of capsule images.\r
502\r
503 @param[in] CapsuleBuffer An array of pointer to capsule images\r
504 @param[in] FileSize An array of UINTN to capsule images size\r
505 @param[in] CapsuleNum The count of capsule images\r
506 @param[out] BlockDescriptors The block descriptors for the capsule images\r
507\r
508 @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.\r
509**/\r
510EFI_STATUS\r
511BuildGatherList (\r
512 IN VOID **CapsuleBuffer,\r
513 IN UINTN *FileSize,\r
514 IN UINTN CapsuleNum,\r
515 OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors\r
516 )\r
517{\r
518 EFI_STATUS Status;\r
519 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors1;\r
520 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors2;\r
521 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorPre;\r
522 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorsHeader;\r
523 EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr;\r
524 UINT8 *TempDataPtr;\r
525 UINTN SizeLeft;\r
526 UINTN Size;\r
527 INT32 Count;\r
528 INT32 Number;\r
529 UINTN Index;\r
530\r
531 TempBlockPtr = NULL;\r
532 BlockDescriptors1 = NULL;\r
533 BlockDescriptors2 = NULL;\r
534 BlockDescriptorPre = NULL;\r
535 BlockDescriptorsHeader = NULL;\r
536\r
537 for (Index = 0; Index < CapsuleNum; Index++) {\r
538 //\r
539 // Allocate memory for the descriptors.\r
540 //\r
541 if (NumberOfDescriptors == 1) {\r
542 Count = 2;\r
543 } else {\r
544 Count = (INT32)(NumberOfDescriptors + 2) / 2;\r
545 }\r
546\r
547 Size = Count * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
548 BlockDescriptors1 = AllocateRuntimeZeroPool (Size);\r
549 if (BlockDescriptors1 == NULL) {\r
550 Print (L"CapsuleApp: failed to allocate memory for descriptors\n");\r
551 Status = EFI_OUT_OF_RESOURCES;\r
552 goto ERREXIT;\r
553 } else {\r
554 Print (L"CapsuleApp: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1);\r
94f5c600 555 Print (L"CapsuleApp: capsule data starts at 0x%X with size 0x%X\n", (UINTN) CapsuleBuffer[Index], FileSize[Index]);\r
592bad04
JY
556 }\r
557\r
558 //\r
559 // Record descirptor header\r
560 //\r
561 if (Index == 0) {\r
562 BlockDescriptorsHeader = BlockDescriptors1;\r
563 }\r
564\r
565 if (BlockDescriptorPre != NULL) {\r
566 BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1;\r
567 BlockDescriptorPre->Length = 0;\r
568 }\r
569\r
570 //\r
571 // Fill them in\r
572 //\r
573 TempBlockPtr = BlockDescriptors1;\r
574 TempDataPtr = CapsuleBuffer[Index];\r
575 SizeLeft = FileSize[Index];\r
576 for (Number = 0; (Number < Count - 1) && (SizeLeft != 0); Number++) {\r
577 //\r
578 // Divide remaining data in half\r
579 //\r
580 if (NumberOfDescriptors != 1) {\r
581 if (SizeLeft == 1) {\r
582 Size = 1;\r
583 } else {\r
584 Size = SizeLeft / 2;\r
585 }\r
586 } else {\r
587 Size = SizeLeft;\r
588 }\r
589 TempBlockPtr->Union.DataBlock = (UINTN)TempDataPtr;\r
590 TempBlockPtr->Length = Size;\r
591 Print (L"CapsuleApp: capsule block/size 0x%X/0x%X\n", (UINTN) TempDataPtr, Size);\r
592 SizeLeft -= Size;\r
593 TempDataPtr += Size;\r
594 TempBlockPtr++;\r
595 }\r
596\r
597 //\r
598 // Allocate the second list, point the first block's last entry to point\r
599 // to this one, and fill this one in. Worst case is that the previous\r
600 // list only had one element that pointed here, so we need at least two\r
601 // elements -- one to point to all the data, another to terminate the list.\r
602 //\r
603 if ((NumberOfDescriptors != 1) && (SizeLeft != 0)) {\r
604 Count = (INT32)(NumberOfDescriptors + 2) - Count;\r
605 if (Count == 1) {\r
606 Count++;\r
607 }\r
608\r
609 Size = Count * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);\r
610 BlockDescriptors2 = AllocateRuntimeZeroPool (Size);\r
611 if (BlockDescriptors2 == NULL) {\r
612 Print (L"CapsuleApp: failed to allocate memory for descriptors\n");\r
613 Status = EFI_OUT_OF_RESOURCES;\r
614 goto ERREXIT;\r
615 }\r
616\r
617 //\r
618 // Point the first list's last element to point to this second list.\r
619 //\r
620 TempBlockPtr->Union.ContinuationPointer = (UINTN) BlockDescriptors2;\r
621\r
622 TempBlockPtr->Length = 0;\r
623 TempBlockPtr = BlockDescriptors2;\r
624 for (Number = 0; Number < Count - 1; Number++) {\r
625 //\r
626 // If second-to-last one, then dump rest to this element\r
627 //\r
628 if (Number == (Count - 2)) {\r
629 Size = SizeLeft;\r
630 } else {\r
631 //\r
632 // Divide remaining data in half\r
633 //\r
634 if (SizeLeft == 1) {\r
635 Size = 1;\r
636 } else {\r
637 Size = SizeLeft / 2;\r
638 }\r
639 }\r
640\r
641 TempBlockPtr->Union.DataBlock = (UINTN)TempDataPtr;\r
642 TempBlockPtr->Length = Size;\r
643 Print (L"CapsuleApp: capsule block/size 0x%X/0x%X\n", (UINTN) TempDataPtr, Size);\r
644 SizeLeft -= Size;\r
645 TempDataPtr += Size;\r
646 TempBlockPtr++;\r
647 if (SizeLeft == 0) {\r
648 break;\r
649 }\r
650 }\r
651 }\r
652\r
653 BlockDescriptorPre = TempBlockPtr;\r
654 BlockDescriptors1 = NULL;\r
655 }\r
656\r
657 //\r
658 // Null-terminate.\r
659 //\r
660 if (TempBlockPtr != NULL) {\r
661 TempBlockPtr->Union.ContinuationPointer = (UINTN)NULL;\r
662 TempBlockPtr->Length = 0;\r
663 *BlockDescriptors = BlockDescriptorsHeader;\r
664 }\r
665\r
666 return EFI_SUCCESS;\r
667\r
668ERREXIT:\r
669 if (BlockDescriptors1 != NULL) {\r
670 FreePool(BlockDescriptors1);\r
671 }\r
672\r
673 if (BlockDescriptors2 != NULL) {\r
674 FreePool(BlockDescriptors2);\r
675 }\r
676\r
677 return Status;\r
678}\r
679\r
680/**\r
681 Clear the Gather list for a list of capsule images.\r
682\r
683 @param[in] BlockDescriptors The block descriptors for the capsule images\r
684 @param[in] CapsuleNum The count of capsule images\r
685**/\r
686VOID\r
687CleanGatherList (\r
688 IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors,\r
689 IN UINTN CapsuleNum\r
690 )\r
691{\r
692 EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr;\r
693 EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr1;\r
694 EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr2;\r
695 UINTN Index;\r
696\r
697 if (BlockDescriptors != NULL) {\r
698 TempBlockPtr1 = BlockDescriptors;\r
699 while (1){\r
700 TempBlockPtr = TempBlockPtr1;\r
701 for (Index = 0; Index < CapsuleNum; Index++) {\r
702 if (TempBlockPtr[Index].Length == 0) {\r
703 break;\r
704 }\r
705 }\r
706\r
707 if (TempBlockPtr[Index].Union.ContinuationPointer == (UINTN)NULL) {\r
708 break;\r
709 }\r
710\r
845f7cfe 711 TempBlockPtr2 = (VOID *) ((UINTN) TempBlockPtr[Index].Union.ContinuationPointer);\r
592bad04
JY
712 FreePool(TempBlockPtr1);\r
713 TempBlockPtr1 = TempBlockPtr2;\r
714 }\r
715 }\r
716}\r
717\r
d9c640b9
SZ
718/**\r
719 Validate if it is valid capsule header\r
720\r
721 This function assumes the caller provided correct CapsuleHeader pointer\r
722 and CapsuleSize.\r
723\r
724 This function validates the fields in EFI_CAPSULE_HEADER.\r
725\r
726 @param[in] CapsuleHeader Points to a capsule header.\r
727 @param[in] CapsuleSize Size of the whole capsule image.\r
728\r
729**/\r
730BOOLEAN\r
731IsValidCapsuleHeader (\r
732 IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
733 IN UINT64 CapsuleSize\r
734 )\r
735{\r
736 if (CapsuleSize < sizeof (EFI_CAPSULE_HEADER)) {\r
737 return FALSE;\r
738 }\r
739 if (CapsuleHeader->CapsuleImageSize != CapsuleSize) {\r
740 return FALSE;\r
741 }\r
742 if (CapsuleHeader->HeaderSize > CapsuleHeader->CapsuleImageSize) {\r
743 return FALSE;\r
744 }\r
745 if (CapsuleHeader->HeaderSize < sizeof (EFI_CAPSULE_HEADER)) {\r
746 return FALSE;\r
747 }\r
748\r
749 return TRUE;\r
750}\r
751\r
592bad04
JY
752/**\r
753 Print APP usage.\r
754**/\r
755VOID\r
756PrintUsage (\r
757 VOID\r
758 )\r
759{\r
760 Print(L"CapsuleApp: usage\n");\r
7043a90e 761 Print(L" CapsuleApp <Capsule...> [-NR]\n");\r
592bad04
JY
762 Print(L" CapsuleApp -S\n");\r
763 Print(L" CapsuleApp -C\n");\r
764 Print(L" CapsuleApp -P\n");\r
765 Print(L" CapsuleApp -E\n");\r
766 Print(L" CapsuleApp -G <BMP> -O <Capsule>\n");\r
767 Print(L" CapsuleApp -N <Capsule> -O <NestedCapsule>\n");\r
768 Print(L" CapsuleApp -D <Capsule>\n");\r
1e09ec09 769 Print(L" CapsuleApp -P GET <ImageTypeId> <Index> -O <FileName>\n");\r
592bad04 770 Print(L"Parameter:\n");\r
7043a90e
SZ
771 Print(L" -NR: No reset will be triggered for the capsule\n");\r
772 Print(L" with CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without CAPSULE_FLAGS_INITIATE_RESET.\n");\r
592bad04
JY
773 Print(L" -S: Dump capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");\r
774 Print(L" which is defined in UEFI specification.\n");\r
0c6f94da 775 Print(L" -C: Clear capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");\r
592bad04 776 Print(L" which is defined in UEFI specification.\n");\r
5410502f
SZ
777 Print(L" -P: Dump UEFI FMP protocol info, or get image with specified\n");\r
778 Print(L" ImageTypeId and index to a file if 'GET' option is used.\n");\r
592bad04 779 Print(L" -E: Dump UEFI ESRT table info.\n");\r
045bb323 780 Print(L" -G: Convert a BMP file to be an UX capsule,\n");\r
592bad04
JY
781 Print(L" according to Windows Firmware Update document\n");\r
782 Print(L" -N: Append a Capsule Header to an existing capsule image,\n");\r
783 Print(L" according to Windows Firmware Update document\n");\r
784 Print(L" -O: Output new Capsule file name\n");\r
045bb323
SZ
785 Print(L" -D: Dump Capsule image header information, image payload information if it is an UX capsule\n");\r
786 Print(L" and FMP header information if it is a FMP capsule.\n");\r
592bad04
JY
787}\r
788\r
789/**\r
790 Update Capsule image.\r
791\r
792 @param[in] ImageHandle The image handle.\r
793 @param[in] SystemTable The system table.\r
794\r
795 @retval EFI_SUCCESS Command completed successfully.\r
796 @retval EFI_INVALID_PARAMETER Command usage error.\r
797 @retval EFI_NOT_FOUND The input file can't be found.\r
798**/\r
799EFI_STATUS\r
800EFIAPI\r
801UefiMain (\r
802 IN EFI_HANDLE ImageHandle,\r
803 IN EFI_SYSTEM_TABLE *SystemTable\r
804 )\r
805{\r
806 EFI_STATUS Status;\r
787f6744 807 RETURN_STATUS RStatus;\r
592bad04
JY
808 UINTN FileSize[MAX_CAPSULE_NUM];\r
809 VOID *CapsuleBuffer[MAX_CAPSULE_NUM];\r
810 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;\r
811 EFI_CAPSULE_HEADER *CapsuleHeaderArray[MAX_CAPSULE_NUM + 1];\r
812 UINT64 MaxCapsuleSize;\r
813 EFI_RESET_TYPE ResetType;\r
814 BOOLEAN NeedReset;\r
7043a90e 815 BOOLEAN NoReset;\r
592bad04
JY
816 CHAR16 *CapsuleName;\r
817 UINTN CapsuleNum;\r
818 UINTN Index;\r
819\r
820 Status = GetArg();\r
821 if (EFI_ERROR(Status)) {\r
822 Print(L"Please use UEFI SHELL to run this application!\n", Status);\r
823 return Status;\r
824 }\r
825 if (Argc < 2) {\r
826 PrintUsage();\r
827 return EFI_INVALID_PARAMETER;\r
828 }\r
829 if (StrCmp(Argv[1], L"-D") == 0) {\r
1c6dd45b
SZ
830 if (Argc < 3) {\r
831 Print(L"CapsuleApp: NO input capsule name.\n");\r
832 return EFI_INVALID_PARAMETER;\r
833 }\r
592bad04
JY
834 Status = DumpCapsule(Argv[2]);\r
835 return Status;\r
836 }\r
837 if (StrCmp(Argv[1], L"-G") == 0) {\r
838 Status = CreateBmpFmp();\r
839 return Status;\r
840 }\r
841 if (StrCmp(Argv[1], L"-N") == 0) {\r
842 Status = CreateNestedFmp();\r
843 return Status;\r
844 }\r
845 if (StrCmp(Argv[1], L"-S") == 0) {\r
846 Status = DmpCapsuleStatusVariable();\r
847 return EFI_SUCCESS;\r
848 }\r
849 if (StrCmp(Argv[1], L"-C") == 0) {\r
850 Status = ClearCapsuleStatusVariable();\r
851 return Status;\r
852 }\r
853 if (StrCmp(Argv[1], L"-P") == 0) {\r
1e09ec09
JY
854 if (Argc == 2) {\r
855 DumpFmpData();\r
856 }\r
857 if (Argc >= 3) {\r
5410502f
SZ
858 if (StrCmp(Argv[2], L"GET") != 0) {\r
859 Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[2]);\r
860 return EFI_UNSUPPORTED;\r
861 } else {\r
862 if (Argc != 7) {\r
863 Print(L"CapsuleApp: Incorrect parameter count.\n");\r
864 return EFI_UNSUPPORTED;\r
865 }\r
866\r
1e09ec09
JY
867 EFI_GUID ImageTypeId;\r
868 UINTN ImageIndex;\r
869 //\r
870 // FMP->GetImage()\r
871 //\r
787f6744
RN
872 RStatus = StrToGuid (Argv[3], &ImageTypeId);\r
873 if (RETURN_ERROR (RStatus) || (Argv[3][GUID_STRING_LENGTH] != L'\0')) {\r
1e09ec09 874 Print (L"Invalid ImageTypeId - %s\n", Argv[3]);\r
787f6744 875 return EFI_INVALID_PARAMETER;\r
1e09ec09
JY
876 }\r
877 ImageIndex = StrDecimalToUintn(Argv[4]);\r
5410502f
SZ
878 if (StrCmp(Argv[5], L"-O") != 0) {\r
879 Print(L"CapsuleApp: NO output file name.\n");\r
880 return EFI_UNSUPPORTED;\r
1e09ec09 881 }\r
5410502f 882 DumpFmpImage(&ImageTypeId, ImageIndex, Argv[6]);\r
1e09ec09
JY
883 }\r
884 }\r
592bad04
JY
885 return EFI_SUCCESS;\r
886 }\r
95dd7a6e 887\r
592bad04
JY
888 if (StrCmp(Argv[1], L"-E") == 0) {\r
889 DumpEsrtData();\r
890 return EFI_SUCCESS;\r
891 }\r
95dd7a6e
SZ
892\r
893 if (Argv[1][0] == L'-') {\r
894 Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[1]);\r
895 return EFI_UNSUPPORTED;\r
896 }\r
897\r
592bad04 898 CapsuleFirstIndex = 1;\r
7043a90e
SZ
899 NoReset = FALSE;\r
900 if ((Argc > 1) && (StrCmp(Argv[Argc - 1], L"-NR") == 0)) {\r
901 NoReset = TRUE;\r
902 CapsuleLastIndex = Argc - 2;\r
903 } else {\r
904 CapsuleLastIndex = Argc - 1;\r
905 }\r
592bad04
JY
906 CapsuleNum = CapsuleLastIndex - CapsuleFirstIndex + 1;\r
907\r
908 if (CapsuleFirstIndex > CapsuleLastIndex) {\r
909 Print(L"CapsuleApp: NO capsule image.\n");\r
910 return EFI_UNSUPPORTED;\r
911 }\r
912 if (CapsuleNum > MAX_CAPSULE_NUM) {\r
913 Print(L"CapsuleApp: Too many capsule images.\n");\r
914 return EFI_UNSUPPORTED;\r
915 }\r
916\r
917 ZeroMem(&CapsuleBuffer, sizeof(CapsuleBuffer));\r
918 ZeroMem(&FileSize, sizeof(FileSize));\r
919 BlockDescriptors = NULL;\r
920\r
921 for (Index = 0; Index < CapsuleNum; Index++) {\r
922 CapsuleName = Argv[CapsuleFirstIndex + Index];\r
923 Status = ReadFileToBuffer(CapsuleName, &FileSize[Index], &CapsuleBuffer[Index]);\r
924 if (EFI_ERROR(Status)) {\r
925 Print(L"CapsuleApp: capsule image (%s) is not found.\n", CapsuleName);\r
926 goto Done;\r
927 }\r
d9c640b9
SZ
928 if (!IsValidCapsuleHeader (CapsuleBuffer[Index], FileSize[Index])) {\r
929 Print(L"CapsuleApp: Capsule image (%s) is not a valid capsule.\n", CapsuleName);\r
930 return EFI_INVALID_PARAMETER;\r
931 }\r
592bad04
JY
932 }\r
933\r
934 //\r
935 // Every capsule use 2 descriptor 1 for data 1 for end\r
936 //\r
937 Status = BuildGatherList(CapsuleBuffer, FileSize, CapsuleNum, &BlockDescriptors);\r
938 if (EFI_ERROR(Status)) {\r
939 goto Done;\r
940 }\r
941\r
942 //\r
943 // Call the runtime service capsule.\r
944 //\r
945 NeedReset = FALSE;\r
946 for (Index = 0; Index < CapsuleNum; Index++) {\r
947 CapsuleHeaderArray[Index] = (EFI_CAPSULE_HEADER *) CapsuleBuffer[Index];\r
948 if ((CapsuleHeaderArray[Index]->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {\r
949 NeedReset = TRUE;\r
950 }\r
951 }\r
952 CapsuleHeaderArray[CapsuleNum] = NULL;\r
953\r
954 //\r
955 // Inquire platform capability of UpdateCapsule.\r
956 //\r
957 Status = gRT->QueryCapsuleCapabilities (CapsuleHeaderArray, CapsuleNum, &MaxCapsuleSize, &ResetType);\r
958 if (EFI_ERROR(Status)) {\r
959 Print (L"CapsuleApp: failed to query capsule capability - %r\n", Status);\r
960 goto Done;\r
961 }\r
962\r
963 for (Index = 0; Index < CapsuleNum; Index++) {\r
964 if (FileSize[Index] > MaxCapsuleSize) {\r
965 Print (L"CapsuleApp: capsule is too large to update, %ld is allowed\n", MaxCapsuleSize);\r
966 Status = EFI_UNSUPPORTED;\r
967 goto Done;\r
968 }\r
969 }\r
970\r
971 //\r
972 // Check whether the input capsule image has the flag of persist across system reset.\r
973 //\r
974 if (NeedReset) {\r
975 Status = gRT->UpdateCapsule(CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors);\r
976 if (Status != EFI_SUCCESS) {\r
977 Print (L"CapsuleApp: failed to update capsule - %r\n", Status);\r
978 goto Done;\r
979 }\r
980 //\r
7043a90e
SZ
981 // For capsule with CAPSULE_FLAGS_PERSIST_ACROSS_RESET + CAPSULE_FLAGS_INITIATE_RESET,\r
982 // a system reset should have been triggered by gRT->UpdateCapsule() calling above.\r
983 //\r
984 // For capsule with CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without CAPSULE_FLAGS_INITIATE_RESET,\r
985 // check if -NR (no-reset) has been specified or not.\r
592bad04 986 //\r
7043a90e
SZ
987 if (!NoReset) {\r
988 //\r
989 // For capsule who has reset flag and no -NR (no-reset) has been specified, after calling UpdateCapsule service,\r
990 // trigger a system reset to process capsule persist across a system reset.\r
991 //\r
992 gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL);\r
993 }\r
592bad04
JY
994 } else {\r
995 //\r
996 // For capsule who has no reset flag, only call UpdateCapsule Service without a\r
997 // system reset. The service will process the capsule immediately.\r
998 //\r
999 Status = gRT->UpdateCapsule (CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors);\r
1000 if (Status != EFI_SUCCESS) {\r
1001 Print (L"CapsuleApp: failed to update capsule - %r\n", Status);\r
1002 }\r
1003 }\r
1004\r
1005 Status = EFI_SUCCESS;\r
1006\r
1007Done:\r
1008 for (Index = 0; Index < CapsuleNum; Index++) {\r
1009 if (CapsuleBuffer[Index] != NULL) {\r
1010 FreePool (CapsuleBuffer[Index]);\r
1011 }\r
1012 }\r
1013\r
1014 CleanGatherList(BlockDescriptors, CapsuleNum);\r
1015\r
1016 return Status;\r
1017}\r