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