MdeModulePkg CapsuleApp: Refine -N option help information
[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
10944bc3 196 Print(L"CapsuleApp: Incorrect parameter count.\n");\r
592bad04
JY
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
10944bc3 390 Print(L"CapsuleApp: Incorrect parameter count.\n");\r
592bad04
JY
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 781 Print(L" according to Windows Firmware Update document\n");\r
e70d9b8c
SZ
782 Print(L" -N: Append a Capsule Header to an existing FMP capsule image\n");\r
783 Print(L" with its ImageTypeId supported by the system,\n");\r
592bad04
JY
784 Print(L" according to Windows Firmware Update document\n");\r
785 Print(L" -O: Output new Capsule file name\n");\r
045bb323
SZ
786 Print(L" -D: Dump Capsule image header information, image payload information if it is an UX capsule\n");\r
787 Print(L" and FMP header information if it is a FMP capsule.\n");\r
592bad04
JY
788}\r
789\r
790/**\r
791 Update Capsule image.\r
792\r
793 @param[in] ImageHandle The image handle.\r
794 @param[in] SystemTable The system table.\r
795\r
796 @retval EFI_SUCCESS Command completed successfully.\r
10944bc3
SZ
797 @retval EFI_UNSUPPORTED Command usage unsupported.\r
798 @retval EFI_INVALID_PARAMETER Command usage invalid.\r
592bad04
JY
799 @retval EFI_NOT_FOUND The input file can't be found.\r
800**/\r
801EFI_STATUS\r
802EFIAPI\r
803UefiMain (\r
804 IN EFI_HANDLE ImageHandle,\r
805 IN EFI_SYSTEM_TABLE *SystemTable\r
806 )\r
807{\r
808 EFI_STATUS Status;\r
787f6744 809 RETURN_STATUS RStatus;\r
592bad04
JY
810 UINTN FileSize[MAX_CAPSULE_NUM];\r
811 VOID *CapsuleBuffer[MAX_CAPSULE_NUM];\r
812 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;\r
4436f722
SZ
813 EFI_CAPSULE_HEADER *CapsuleHeaderArray[MAX_CAPSULE_NUM + 1];\r
814 UINT64 MaxCapsuleSize;\r
815 EFI_RESET_TYPE ResetType;\r
816 BOOLEAN NeedReset;\r
817 BOOLEAN NoReset;\r
818 CHAR16 *CapsuleName;\r
819 UINTN CapsuleNum;\r
820 UINTN Index;\r
821 EFI_GUID ImageTypeId;\r
822 UINTN ImageIndex;\r
592bad04
JY
823\r
824 Status = GetArg();\r
825 if (EFI_ERROR(Status)) {\r
826 Print(L"Please use UEFI SHELL to run this application!\n", Status);\r
827 return Status;\r
828 }\r
829 if (Argc < 2) {\r
830 PrintUsage();\r
10944bc3 831 return EFI_UNSUPPORTED;\r
592bad04
JY
832 }\r
833 if (StrCmp(Argv[1], L"-D") == 0) {\r
10944bc3
SZ
834 if (Argc != 3) {\r
835 Print(L"CapsuleApp: Incorrect parameter count.\n");\r
836 return EFI_UNSUPPORTED;\r
1c6dd45b 837 }\r
592bad04
JY
838 Status = DumpCapsule(Argv[2]);\r
839 return Status;\r
840 }\r
841 if (StrCmp(Argv[1], L"-G") == 0) {\r
842 Status = CreateBmpFmp();\r
843 return Status;\r
844 }\r
845 if (StrCmp(Argv[1], L"-N") == 0) {\r
846 Status = CreateNestedFmp();\r
847 return Status;\r
848 }\r
849 if (StrCmp(Argv[1], L"-S") == 0) {\r
850 Status = DmpCapsuleStatusVariable();\r
851 return EFI_SUCCESS;\r
852 }\r
853 if (StrCmp(Argv[1], L"-C") == 0) {\r
854 Status = ClearCapsuleStatusVariable();\r
855 return Status;\r
856 }\r
857 if (StrCmp(Argv[1], L"-P") == 0) {\r
1e09ec09
JY
858 if (Argc == 2) {\r
859 DumpFmpData();\r
860 }\r
861 if (Argc >= 3) {\r
5410502f
SZ
862 if (StrCmp(Argv[2], L"GET") != 0) {\r
863 Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[2]);\r
864 return EFI_UNSUPPORTED;\r
865 } else {\r
866 if (Argc != 7) {\r
867 Print(L"CapsuleApp: Incorrect parameter count.\n");\r
868 return EFI_UNSUPPORTED;\r
869 }\r
870\r
1e09ec09
JY
871 //\r
872 // FMP->GetImage()\r
873 //\r
787f6744
RN
874 RStatus = StrToGuid (Argv[3], &ImageTypeId);\r
875 if (RETURN_ERROR (RStatus) || (Argv[3][GUID_STRING_LENGTH] != L'\0')) {\r
1e09ec09 876 Print (L"Invalid ImageTypeId - %s\n", Argv[3]);\r
787f6744 877 return EFI_INVALID_PARAMETER;\r
1e09ec09
JY
878 }\r
879 ImageIndex = StrDecimalToUintn(Argv[4]);\r
5410502f
SZ
880 if (StrCmp(Argv[5], L"-O") != 0) {\r
881 Print(L"CapsuleApp: NO output file name.\n");\r
882 return EFI_UNSUPPORTED;\r
1e09ec09 883 }\r
5410502f 884 DumpFmpImage(&ImageTypeId, ImageIndex, Argv[6]);\r
1e09ec09
JY
885 }\r
886 }\r
592bad04
JY
887 return EFI_SUCCESS;\r
888 }\r
95dd7a6e 889\r
592bad04
JY
890 if (StrCmp(Argv[1], L"-E") == 0) {\r
891 DumpEsrtData();\r
892 return EFI_SUCCESS;\r
893 }\r
95dd7a6e
SZ
894\r
895 if (Argv[1][0] == L'-') {\r
896 Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[1]);\r
897 return EFI_UNSUPPORTED;\r
898 }\r
899\r
592bad04 900 CapsuleFirstIndex = 1;\r
7043a90e
SZ
901 NoReset = FALSE;\r
902 if ((Argc > 1) && (StrCmp(Argv[Argc - 1], L"-NR") == 0)) {\r
903 NoReset = TRUE;\r
904 CapsuleLastIndex = Argc - 2;\r
905 } else {\r
906 CapsuleLastIndex = Argc - 1;\r
907 }\r
592bad04
JY
908 CapsuleNum = CapsuleLastIndex - CapsuleFirstIndex + 1;\r
909\r
910 if (CapsuleFirstIndex > CapsuleLastIndex) {\r
911 Print(L"CapsuleApp: NO capsule image.\n");\r
912 return EFI_UNSUPPORTED;\r
913 }\r
914 if (CapsuleNum > MAX_CAPSULE_NUM) {\r
915 Print(L"CapsuleApp: Too many capsule images.\n");\r
916 return EFI_UNSUPPORTED;\r
917 }\r
918\r
919 ZeroMem(&CapsuleBuffer, sizeof(CapsuleBuffer));\r
920 ZeroMem(&FileSize, sizeof(FileSize));\r
921 BlockDescriptors = NULL;\r
922\r
923 for (Index = 0; Index < CapsuleNum; Index++) {\r
924 CapsuleName = Argv[CapsuleFirstIndex + Index];\r
925 Status = ReadFileToBuffer(CapsuleName, &FileSize[Index], &CapsuleBuffer[Index]);\r
926 if (EFI_ERROR(Status)) {\r
927 Print(L"CapsuleApp: capsule image (%s) is not found.\n", CapsuleName);\r
928 goto Done;\r
929 }\r
d9c640b9
SZ
930 if (!IsValidCapsuleHeader (CapsuleBuffer[Index], FileSize[Index])) {\r
931 Print(L"CapsuleApp: Capsule image (%s) is not a valid capsule.\n", CapsuleName);\r
932 return EFI_INVALID_PARAMETER;\r
933 }\r
592bad04
JY
934 }\r
935\r
936 //\r
937 // Every capsule use 2 descriptor 1 for data 1 for end\r
938 //\r
939 Status = BuildGatherList(CapsuleBuffer, FileSize, CapsuleNum, &BlockDescriptors);\r
940 if (EFI_ERROR(Status)) {\r
941 goto Done;\r
942 }\r
943\r
944 //\r
945 // Call the runtime service capsule.\r
946 //\r
947 NeedReset = FALSE;\r
948 for (Index = 0; Index < CapsuleNum; Index++) {\r
949 CapsuleHeaderArray[Index] = (EFI_CAPSULE_HEADER *) CapsuleBuffer[Index];\r
950 if ((CapsuleHeaderArray[Index]->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {\r
951 NeedReset = TRUE;\r
952 }\r
953 }\r
954 CapsuleHeaderArray[CapsuleNum] = NULL;\r
955\r
956 //\r
957 // Inquire platform capability of UpdateCapsule.\r
958 //\r
959 Status = gRT->QueryCapsuleCapabilities (CapsuleHeaderArray, CapsuleNum, &MaxCapsuleSize, &ResetType);\r
960 if (EFI_ERROR(Status)) {\r
961 Print (L"CapsuleApp: failed to query capsule capability - %r\n", Status);\r
962 goto Done;\r
963 }\r
964\r
965 for (Index = 0; Index < CapsuleNum; Index++) {\r
966 if (FileSize[Index] > MaxCapsuleSize) {\r
967 Print (L"CapsuleApp: capsule is too large to update, %ld is allowed\n", MaxCapsuleSize);\r
968 Status = EFI_UNSUPPORTED;\r
969 goto Done;\r
970 }\r
971 }\r
972\r
973 //\r
974 // Check whether the input capsule image has the flag of persist across system reset.\r
975 //\r
976 if (NeedReset) {\r
977 Status = gRT->UpdateCapsule(CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors);\r
978 if (Status != EFI_SUCCESS) {\r
979 Print (L"CapsuleApp: failed to update capsule - %r\n", Status);\r
980 goto Done;\r
981 }\r
982 //\r
7043a90e
SZ
983 // For capsule with CAPSULE_FLAGS_PERSIST_ACROSS_RESET + CAPSULE_FLAGS_INITIATE_RESET,\r
984 // a system reset should have been triggered by gRT->UpdateCapsule() calling above.\r
985 //\r
986 // For capsule with CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without CAPSULE_FLAGS_INITIATE_RESET,\r
987 // check if -NR (no-reset) has been specified or not.\r
592bad04 988 //\r
7043a90e
SZ
989 if (!NoReset) {\r
990 //\r
991 // For capsule who has reset flag and no -NR (no-reset) has been specified, after calling UpdateCapsule service,\r
992 // trigger a system reset to process capsule persist across a system reset.\r
993 //\r
994 gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL);\r
995 }\r
592bad04
JY
996 } else {\r
997 //\r
998 // For capsule who has no reset flag, only call UpdateCapsule Service without a\r
999 // system reset. The service will process the capsule immediately.\r
1000 //\r
1001 Status = gRT->UpdateCapsule (CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors);\r
1002 if (Status != EFI_SUCCESS) {\r
1003 Print (L"CapsuleApp: failed to update capsule - %r\n", Status);\r
1004 }\r
1005 }\r
1006\r
1007 Status = EFI_SUCCESS;\r
1008\r
1009Done:\r
1010 for (Index = 0; Index < CapsuleNum; Index++) {\r
1011 if (CapsuleBuffer[Index] != NULL) {\r
1012 FreePool (CapsuleBuffer[Index]);\r
1013 }\r
1014 }\r
1015\r
1016 CleanGatherList(BlockDescriptors, CapsuleNum);\r
1017\r
1018 return Status;\r
1019}\r