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