]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
MdeModulePkg CapsuleApp: Check capsule header for -D and -N options
[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 Validate if it is valid capsule header
367
368 This function assumes the caller provided correct CapsuleHeader pointer
369 and CapsuleSize.
370
371 This function validates the fields in EFI_CAPSULE_HEADER.
372
373 @param[in] CapsuleHeader Points to a capsule header.
374 @param[in] CapsuleSize Size of the whole capsule image.
375
376 **/
377 BOOLEAN
378 IsValidCapsuleHeader (
379 IN EFI_CAPSULE_HEADER *CapsuleHeader,
380 IN UINT64 CapsuleSize
381 )
382 {
383 if (CapsuleSize < sizeof (EFI_CAPSULE_HEADER)) {
384 return FALSE;
385 }
386 if (CapsuleHeader->CapsuleImageSize != CapsuleSize) {
387 return FALSE;
388 }
389 if (CapsuleHeader->HeaderSize > CapsuleHeader->CapsuleImageSize) {
390 return FALSE;
391 }
392 if (CapsuleHeader->HeaderSize < sizeof (EFI_CAPSULE_HEADER)) {
393 return FALSE;
394 }
395
396 return TRUE;
397 }
398
399 /**
400 Return if this CapsuleGuid is a FMP capsule GUID or not.
401
402 @param[in] CapsuleGuid A pointer to EFI_GUID
403
404 @retval TRUE It is a FMP capsule GUID.
405 @retval FALSE It is not a FMP capsule GUID.
406 **/
407 BOOLEAN
408 IsFmpCapsuleGuid (
409 IN EFI_GUID *CapsuleGuid
410 )
411 {
412 if (CompareGuid(&gEfiFmpCapsuleGuid, CapsuleGuid)) {
413 return TRUE;
414 }
415
416 return FALSE;
417 }
418
419 /**
420 Append a capsule header on top of current image.
421 This function follows Windows UEFI Firmware Update Platform document.
422
423 @retval EFI_SUCCESS The capsule header is appended.
424 @retval EFI_UNSUPPORTED Input parameter is not valid.
425 @retval EFI_OUT_OF_RESOURCES No enough resource to append capsule header.
426 **/
427 EFI_STATUS
428 CreateNestedFmp (
429 VOID
430 )
431 {
432 CHAR16 *OutputCapsuleName;
433 VOID *CapsuleBuffer;
434 UINTN FileSize;
435 CHAR16 *CapsuleName;
436 UINT8 *FullCapsuleBuffer;
437 UINTN FullCapsuleBufferSize;
438 EFI_CAPSULE_HEADER *NestedCapsuleHeader;
439 EFI_GUID *ImageTypeId;
440 UINT32 FwType;
441 EFI_STATUS Status;
442
443 if (Argc != 5) {
444 Print(L"CapsuleApp: Incorrect parameter count.\n");
445 return EFI_UNSUPPORTED;
446 }
447
448 if (StrCmp(Argv[3], L"-O") != 0) {
449 Print(L"CapsuleApp: NO output capsule name.\n");
450 return EFI_UNSUPPORTED;
451 }
452 OutputCapsuleName = Argv[4];
453
454 CapsuleBuffer = NULL;
455 FileSize = 0;
456 FullCapsuleBuffer = NULL;
457
458 CapsuleName = Argv[2];
459 Status = ReadFileToBuffer(CapsuleName, &FileSize, &CapsuleBuffer);
460 if (EFI_ERROR(Status)) {
461 Print(L"CapsuleApp: Capsule image (%s) is not found.\n", CapsuleName);
462 goto Done;
463 }
464 if (!IsValidCapsuleHeader (CapsuleBuffer, FileSize)) {
465 Print(L"CapsuleApp: Capsule image (%s) is not a valid capsule.\n", CapsuleName);
466 Status = EFI_INVALID_PARAMETER;
467 goto Done;
468 }
469
470 if (!IsFmpCapsuleGuid (&((EFI_CAPSULE_HEADER *) CapsuleBuffer)->CapsuleGuid)) {
471 Print(L"CapsuleApp: Capsule image (%s) is not a FMP capsule.\n", CapsuleName);
472 Status = EFI_INVALID_PARAMETER;
473 goto Done;
474 }
475
476 ImageTypeId = GetCapsuleImageTypeId(CapsuleBuffer);
477 if (ImageTypeId == NULL) {
478 Print(L"CapsuleApp: Capsule ImageTypeId is not found.\n");
479 Status = EFI_INVALID_PARAMETER;
480 goto Done;
481 }
482 FwType = GetEsrtFwType(ImageTypeId);
483 if ((FwType != ESRT_FW_TYPE_SYSTEMFIRMWARE) && (FwType != ESRT_FW_TYPE_DEVICEFIRMWARE)) {
484 Print(L"CapsuleApp: Capsule FwType is invalid.\n");
485 Status = EFI_INVALID_PARAMETER;
486 goto Done;
487 }
488
489 FullCapsuleBufferSize = NESTED_CAPSULE_HEADER_SIZE + FileSize;
490 FullCapsuleBuffer = AllocatePool(FullCapsuleBufferSize);
491 if (FullCapsuleBuffer == NULL) {
492 Print(L"CapsuleApp: Capsule Buffer size (0x%x) too big.\n", FullCapsuleBufferSize);
493 Status = EFI_OUT_OF_RESOURCES;
494 goto Done;
495 }
496
497 NestedCapsuleHeader = (EFI_CAPSULE_HEADER *)FullCapsuleBuffer;
498 ZeroMem(NestedCapsuleHeader, NESTED_CAPSULE_HEADER_SIZE);
499 CopyGuid(&NestedCapsuleHeader->CapsuleGuid, ImageTypeId);
500 NestedCapsuleHeader->HeaderSize = NESTED_CAPSULE_HEADER_SIZE;
501 NestedCapsuleHeader->Flags = (FwType == ESRT_FW_TYPE_SYSTEMFIRMWARE) ? SYSTEM_FIRMWARE_FLAG : DEVICE_FIRMWARE_FLAG;
502 NestedCapsuleHeader->CapsuleImageSize = (UINT32)FullCapsuleBufferSize;
503
504 CopyMem((UINT8 *)NestedCapsuleHeader + NestedCapsuleHeader->HeaderSize, CapsuleBuffer, FileSize);
505
506 Status = WriteFileFromBuffer(OutputCapsuleName, FullCapsuleBufferSize, FullCapsuleBuffer);
507 Print(L"CapsuleApp: Write %s %r\n", OutputCapsuleName, Status);
508
509 Done:
510 if (CapsuleBuffer != NULL) {
511 FreePool(CapsuleBuffer);
512 }
513
514 if (FullCapsuleBuffer != NULL) {
515 FreePool(FullCapsuleBuffer);
516 }
517
518 return Status;
519 }
520
521
522 /**
523 Clear capsule status variable.
524
525 @retval EFI_SUCCESS The capsule status variable is cleared.
526 **/
527 EFI_STATUS
528 ClearCapsuleStatusVariable (
529 VOID
530 )
531 {
532 EFI_STATUS Status;
533 UINT32 Index;
534 CHAR16 CapsuleVarName[20];
535 CHAR16 *TempVarName;
536 BOOLEAN Found;
537
538 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), L"Capsule");
539 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
540 Index = 0;
541
542 Found = FALSE;
543 while (TRUE) {
544 UnicodeSPrint (TempVarName, 5 * sizeof(CHAR16), L"%04x", Index);
545
546 Status = gRT->SetVariable (
547 CapsuleVarName,
548 &gEfiCapsuleReportGuid,
549 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
550 0,
551 (VOID *)NULL
552 );
553 if (Status == EFI_NOT_FOUND) {
554 //
555 // There is no more capsule variables, quit
556 //
557 break;
558 }
559 Found = TRUE;
560
561 Print (L"Clear %s %r\n", CapsuleVarName, Status);
562
563 Index++;
564 if (Index > 0xFFFF) {
565 break;
566 }
567 }
568
569 if (!Found) {
570 Print (L"No any Capsule#### variable found\n");
571 }
572
573 return EFI_SUCCESS;
574 }
575
576 /**
577 Build Gather list for a list of capsule images.
578
579 @param[in] CapsuleBuffer An array of pointer to capsule images
580 @param[in] FileSize An array of UINTN to capsule images size
581 @param[in] CapsuleNum The count of capsule images
582 @param[out] BlockDescriptors The block descriptors for the capsule images
583
584 @retval EFI_SUCCESS The block descriptors for the capsule images are constructed.
585 **/
586 EFI_STATUS
587 BuildGatherList (
588 IN VOID **CapsuleBuffer,
589 IN UINTN *FileSize,
590 IN UINTN CapsuleNum,
591 OUT EFI_CAPSULE_BLOCK_DESCRIPTOR **BlockDescriptors
592 )
593 {
594 EFI_STATUS Status;
595 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors1;
596 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors2;
597 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorPre;
598 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptorsHeader;
599 EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr;
600 UINT8 *TempDataPtr;
601 UINTN SizeLeft;
602 UINTN Size;
603 INT32 Count;
604 INT32 Number;
605 UINTN Index;
606
607 TempBlockPtr = NULL;
608 BlockDescriptors1 = NULL;
609 BlockDescriptors2 = NULL;
610 BlockDescriptorPre = NULL;
611 BlockDescriptorsHeader = NULL;
612
613 for (Index = 0; Index < CapsuleNum; Index++) {
614 //
615 // Allocate memory for the descriptors.
616 //
617 if (NumberOfDescriptors == 1) {
618 Count = 2;
619 } else {
620 Count = (INT32)(NumberOfDescriptors + 2) / 2;
621 }
622
623 Size = Count * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
624 BlockDescriptors1 = AllocateRuntimeZeroPool (Size);
625 if (BlockDescriptors1 == NULL) {
626 Print (L"CapsuleApp: failed to allocate memory for descriptors\n");
627 Status = EFI_OUT_OF_RESOURCES;
628 goto ERREXIT;
629 } else {
630 Print (L"CapsuleApp: creating capsule descriptors at 0x%X\n", (UINTN) BlockDescriptors1);
631 Print (L"CapsuleApp: capsule data starts at 0x%X with size 0x%X\n", (UINTN) CapsuleBuffer[Index], FileSize[Index]);
632 }
633
634 //
635 // Record descirptor header
636 //
637 if (Index == 0) {
638 BlockDescriptorsHeader = BlockDescriptors1;
639 }
640
641 if (BlockDescriptorPre != NULL) {
642 BlockDescriptorPre->Union.ContinuationPointer = (UINTN) BlockDescriptors1;
643 BlockDescriptorPre->Length = 0;
644 }
645
646 //
647 // Fill them in
648 //
649 TempBlockPtr = BlockDescriptors1;
650 TempDataPtr = CapsuleBuffer[Index];
651 SizeLeft = FileSize[Index];
652 for (Number = 0; (Number < Count - 1) && (SizeLeft != 0); Number++) {
653 //
654 // Divide remaining data in half
655 //
656 if (NumberOfDescriptors != 1) {
657 if (SizeLeft == 1) {
658 Size = 1;
659 } else {
660 Size = SizeLeft / 2;
661 }
662 } else {
663 Size = SizeLeft;
664 }
665 TempBlockPtr->Union.DataBlock = (UINTN)TempDataPtr;
666 TempBlockPtr->Length = Size;
667 Print (L"CapsuleApp: capsule block/size 0x%X/0x%X\n", (UINTN) TempDataPtr, Size);
668 SizeLeft -= Size;
669 TempDataPtr += Size;
670 TempBlockPtr++;
671 }
672
673 //
674 // Allocate the second list, point the first block's last entry to point
675 // to this one, and fill this one in. Worst case is that the previous
676 // list only had one element that pointed here, so we need at least two
677 // elements -- one to point to all the data, another to terminate the list.
678 //
679 if ((NumberOfDescriptors != 1) && (SizeLeft != 0)) {
680 Count = (INT32)(NumberOfDescriptors + 2) - Count;
681 if (Count == 1) {
682 Count++;
683 }
684
685 Size = Count * sizeof (EFI_CAPSULE_BLOCK_DESCRIPTOR);
686 BlockDescriptors2 = AllocateRuntimeZeroPool (Size);
687 if (BlockDescriptors2 == NULL) {
688 Print (L"CapsuleApp: failed to allocate memory for descriptors\n");
689 Status = EFI_OUT_OF_RESOURCES;
690 goto ERREXIT;
691 }
692
693 //
694 // Point the first list's last element to point to this second list.
695 //
696 TempBlockPtr->Union.ContinuationPointer = (UINTN) BlockDescriptors2;
697
698 TempBlockPtr->Length = 0;
699 TempBlockPtr = BlockDescriptors2;
700 for (Number = 0; Number < Count - 1; Number++) {
701 //
702 // If second-to-last one, then dump rest to this element
703 //
704 if (Number == (Count - 2)) {
705 Size = SizeLeft;
706 } else {
707 //
708 // Divide remaining data in half
709 //
710 if (SizeLeft == 1) {
711 Size = 1;
712 } else {
713 Size = SizeLeft / 2;
714 }
715 }
716
717 TempBlockPtr->Union.DataBlock = (UINTN)TempDataPtr;
718 TempBlockPtr->Length = Size;
719 Print (L"CapsuleApp: capsule block/size 0x%X/0x%X\n", (UINTN) TempDataPtr, Size);
720 SizeLeft -= Size;
721 TempDataPtr += Size;
722 TempBlockPtr++;
723 if (SizeLeft == 0) {
724 break;
725 }
726 }
727 }
728
729 BlockDescriptorPre = TempBlockPtr;
730 BlockDescriptors1 = NULL;
731 }
732
733 //
734 // Null-terminate.
735 //
736 if (TempBlockPtr != NULL) {
737 TempBlockPtr->Union.ContinuationPointer = (UINTN)NULL;
738 TempBlockPtr->Length = 0;
739 *BlockDescriptors = BlockDescriptorsHeader;
740 }
741
742 return EFI_SUCCESS;
743
744 ERREXIT:
745 if (BlockDescriptors1 != NULL) {
746 FreePool(BlockDescriptors1);
747 }
748
749 if (BlockDescriptors2 != NULL) {
750 FreePool(BlockDescriptors2);
751 }
752
753 return Status;
754 }
755
756 /**
757 Clear the Gather list for a list of capsule images.
758
759 @param[in] BlockDescriptors The block descriptors for the capsule images
760 @param[in] CapsuleNum The count of capsule images
761 **/
762 VOID
763 CleanGatherList (
764 IN EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors,
765 IN UINTN CapsuleNum
766 )
767 {
768 EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr;
769 EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr1;
770 EFI_CAPSULE_BLOCK_DESCRIPTOR *TempBlockPtr2;
771 UINTN Index;
772
773 if (BlockDescriptors != NULL) {
774 TempBlockPtr1 = BlockDescriptors;
775 while (1){
776 TempBlockPtr = TempBlockPtr1;
777 for (Index = 0; Index < CapsuleNum; Index++) {
778 if (TempBlockPtr[Index].Length == 0) {
779 break;
780 }
781 }
782
783 if (TempBlockPtr[Index].Union.ContinuationPointer == (UINTN)NULL) {
784 break;
785 }
786
787 TempBlockPtr2 = (VOID *) ((UINTN) TempBlockPtr[Index].Union.ContinuationPointer);
788 FreePool(TempBlockPtr1);
789 TempBlockPtr1 = TempBlockPtr2;
790 }
791 }
792 }
793
794 /**
795 Print APP usage.
796 **/
797 VOID
798 PrintUsage (
799 VOID
800 )
801 {
802 Print(L"CapsuleApp: usage\n");
803 Print(L" CapsuleApp <Capsule...> [-NR]\n");
804 Print(L" CapsuleApp -S\n");
805 Print(L" CapsuleApp -C\n");
806 Print(L" CapsuleApp -P\n");
807 Print(L" CapsuleApp -E\n");
808 Print(L" CapsuleApp -G <BMP> -O <Capsule>\n");
809 Print(L" CapsuleApp -N <Capsule> -O <NestedCapsule>\n");
810 Print(L" CapsuleApp -D <Capsule>\n");
811 Print(L" CapsuleApp -P GET <ImageTypeId> <Index> -O <FileName>\n");
812 Print(L"Parameter:\n");
813 Print(L" -NR: No reset will be triggered for the capsule with\n");
814 Print(L" CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without\n");
815 Print(L" CAPSULE_FLAGS_INITIATE_RESET.\n");
816 Print(L" -S: Dump capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");
817 Print(L" which is defined in UEFI specification.\n");
818 Print(L" -C: Clear capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");
819 Print(L" which is defined in UEFI specification.\n");
820 Print(L" -P: Dump UEFI FMP protocol info, or get image with specified\n");
821 Print(L" ImageTypeId and Index (decimal format) to a file if 'GET'\n");
822 Print(L" option is used.\n");
823 Print(L" -E: Dump UEFI ESRT table info.\n");
824 Print(L" -G: Convert a BMP file to be an UX capsule,\n");
825 Print(L" according to Windows Firmware Update document\n");
826 Print(L" -N: Append a Capsule Header to an existing FMP capsule image\n");
827 Print(L" with its ImageTypeId supported by the system,\n");
828 Print(L" according to Windows Firmware Update document\n");
829 Print(L" -O: Output new Capsule file name\n");
830 Print(L" -D: Dump Capsule image header information, image payload\n");
831 Print(L" information if it is an UX capsule and FMP header\n");
832 Print(L" information if it is a FMP capsule.\n");
833 }
834
835 /**
836 Update Capsule image.
837
838 @param[in] ImageHandle The image handle.
839 @param[in] SystemTable The system table.
840
841 @retval EFI_SUCCESS Command completed successfully.
842 @retval EFI_UNSUPPORTED Command usage unsupported.
843 @retval EFI_INVALID_PARAMETER Command usage invalid.
844 @retval EFI_NOT_FOUND The input file can't be found.
845 **/
846 EFI_STATUS
847 EFIAPI
848 UefiMain (
849 IN EFI_HANDLE ImageHandle,
850 IN EFI_SYSTEM_TABLE *SystemTable
851 )
852 {
853 EFI_STATUS Status;
854 RETURN_STATUS RStatus;
855 UINTN FileSize[MAX_CAPSULE_NUM];
856 VOID *CapsuleBuffer[MAX_CAPSULE_NUM];
857 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;
858 EFI_CAPSULE_HEADER *CapsuleHeaderArray[MAX_CAPSULE_NUM + 1];
859 UINT64 MaxCapsuleSize;
860 EFI_RESET_TYPE ResetType;
861 BOOLEAN NeedReset;
862 BOOLEAN NoReset;
863 CHAR16 *CapsuleName;
864 UINTN CapsuleNum;
865 UINTN Index;
866 EFI_GUID ImageTypeId;
867 UINTN ImageIndex;
868
869 Status = GetArg();
870 if (EFI_ERROR(Status)) {
871 Print(L"Please use UEFI SHELL to run this application!\n", Status);
872 return Status;
873 }
874 if (Argc < 2) {
875 PrintUsage();
876 return EFI_UNSUPPORTED;
877 }
878 if (StrCmp(Argv[1], L"-D") == 0) {
879 if (Argc != 3) {
880 Print(L"CapsuleApp: Incorrect parameter count.\n");
881 return EFI_UNSUPPORTED;
882 }
883 Status = DumpCapsule(Argv[2]);
884 return Status;
885 }
886 if (StrCmp(Argv[1], L"-G") == 0) {
887 Status = CreateBmpFmp();
888 return Status;
889 }
890 if (StrCmp(Argv[1], L"-N") == 0) {
891 Status = CreateNestedFmp();
892 return Status;
893 }
894 if (StrCmp(Argv[1], L"-S") == 0) {
895 Status = DumpCapsuleStatusVariable();
896 return EFI_SUCCESS;
897 }
898 if (StrCmp(Argv[1], L"-C") == 0) {
899 Status = ClearCapsuleStatusVariable();
900 return Status;
901 }
902 if (StrCmp(Argv[1], L"-P") == 0) {
903 if (Argc == 2) {
904 DumpFmpData();
905 }
906 if (Argc >= 3) {
907 if (StrCmp(Argv[2], L"GET") != 0) {
908 Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[2]);
909 return EFI_UNSUPPORTED;
910 } else {
911 if (Argc != 7) {
912 Print(L"CapsuleApp: Incorrect parameter count.\n");
913 return EFI_UNSUPPORTED;
914 }
915
916 //
917 // FMP->GetImage()
918 //
919 RStatus = StrToGuid (Argv[3], &ImageTypeId);
920 if (RETURN_ERROR (RStatus) || (Argv[3][GUID_STRING_LENGTH] != L'\0')) {
921 Print (L"Invalid ImageTypeId - %s\n", Argv[3]);
922 return EFI_INVALID_PARAMETER;
923 }
924 ImageIndex = StrDecimalToUintn(Argv[4]);
925 if (StrCmp(Argv[5], L"-O") != 0) {
926 Print(L"CapsuleApp: NO output file name.\n");
927 return EFI_UNSUPPORTED;
928 }
929 DumpFmpImage(&ImageTypeId, ImageIndex, Argv[6]);
930 }
931 }
932 return EFI_SUCCESS;
933 }
934
935 if (StrCmp(Argv[1], L"-E") == 0) {
936 DumpEsrtData();
937 return EFI_SUCCESS;
938 }
939
940 if (Argv[1][0] == L'-') {
941 Print(L"CapsuleApp: Unrecognized option(%s).\n", Argv[1]);
942 return EFI_UNSUPPORTED;
943 }
944
945 CapsuleFirstIndex = 1;
946 NoReset = FALSE;
947 if ((Argc > 1) && (StrCmp(Argv[Argc - 1], L"-NR") == 0)) {
948 NoReset = TRUE;
949 CapsuleLastIndex = Argc - 2;
950 } else {
951 CapsuleLastIndex = Argc - 1;
952 }
953 CapsuleNum = CapsuleLastIndex - CapsuleFirstIndex + 1;
954
955 if (CapsuleFirstIndex > CapsuleLastIndex) {
956 Print(L"CapsuleApp: NO capsule image.\n");
957 return EFI_UNSUPPORTED;
958 }
959 if (CapsuleNum > MAX_CAPSULE_NUM) {
960 Print(L"CapsuleApp: Too many capsule images.\n");
961 return EFI_UNSUPPORTED;
962 }
963
964 ZeroMem(&CapsuleBuffer, sizeof(CapsuleBuffer));
965 ZeroMem(&FileSize, sizeof(FileSize));
966 BlockDescriptors = NULL;
967
968 for (Index = 0; Index < CapsuleNum; Index++) {
969 CapsuleName = Argv[CapsuleFirstIndex + Index];
970 Status = ReadFileToBuffer(CapsuleName, &FileSize[Index], &CapsuleBuffer[Index]);
971 if (EFI_ERROR(Status)) {
972 Print(L"CapsuleApp: capsule image (%s) is not found.\n", CapsuleName);
973 goto Done;
974 }
975 if (!IsValidCapsuleHeader (CapsuleBuffer[Index], FileSize[Index])) {
976 Print(L"CapsuleApp: Capsule image (%s) is not a valid capsule.\n", CapsuleName);
977 return EFI_INVALID_PARAMETER;
978 }
979 }
980
981 //
982 // Every capsule use 2 descriptor 1 for data 1 for end
983 //
984 Status = BuildGatherList(CapsuleBuffer, FileSize, CapsuleNum, &BlockDescriptors);
985 if (EFI_ERROR(Status)) {
986 goto Done;
987 }
988
989 //
990 // Call the runtime service capsule.
991 //
992 NeedReset = FALSE;
993 for (Index = 0; Index < CapsuleNum; Index++) {
994 CapsuleHeaderArray[Index] = (EFI_CAPSULE_HEADER *) CapsuleBuffer[Index];
995 if ((CapsuleHeaderArray[Index]->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
996 NeedReset = TRUE;
997 }
998 }
999 CapsuleHeaderArray[CapsuleNum] = NULL;
1000
1001 //
1002 // Inquire platform capability of UpdateCapsule.
1003 //
1004 Status = gRT->QueryCapsuleCapabilities (CapsuleHeaderArray, CapsuleNum, &MaxCapsuleSize, &ResetType);
1005 if (EFI_ERROR(Status)) {
1006 Print (L"CapsuleApp: failed to query capsule capability - %r\n", Status);
1007 goto Done;
1008 }
1009
1010 for (Index = 0; Index < CapsuleNum; Index++) {
1011 if (FileSize[Index] > MaxCapsuleSize) {
1012 Print (L"CapsuleApp: capsule is too large to update, %ld is allowed\n", MaxCapsuleSize);
1013 Status = EFI_UNSUPPORTED;
1014 goto Done;
1015 }
1016 }
1017
1018 //
1019 // Check whether the input capsule image has the flag of persist across system reset.
1020 //
1021 if (NeedReset) {
1022 Status = gRT->UpdateCapsule(CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors);
1023 if (Status != EFI_SUCCESS) {
1024 Print (L"CapsuleApp: failed to update capsule - %r\n", Status);
1025 goto Done;
1026 }
1027 //
1028 // For capsule with CAPSULE_FLAGS_PERSIST_ACROSS_RESET + CAPSULE_FLAGS_INITIATE_RESET,
1029 // a system reset should have been triggered by gRT->UpdateCapsule() calling above.
1030 //
1031 // For capsule with CAPSULE_FLAGS_PERSIST_ACROSS_RESET and without CAPSULE_FLAGS_INITIATE_RESET,
1032 // check if -NR (no-reset) has been specified or not.
1033 //
1034 if (!NoReset) {
1035 //
1036 // For capsule who has reset flag and no -NR (no-reset) has been specified, after calling UpdateCapsule service,
1037 // trigger a system reset to process capsule persist across a system reset.
1038 //
1039 gRT->ResetSystem (ResetType, EFI_SUCCESS, 0, NULL);
1040 }
1041 } else {
1042 //
1043 // For capsule who has no reset flag, only call UpdateCapsule Service without a
1044 // system reset. The service will process the capsule immediately.
1045 //
1046 Status = gRT->UpdateCapsule (CapsuleHeaderArray,CapsuleNum,(UINTN) BlockDescriptors);
1047 if (Status != EFI_SUCCESS) {
1048 Print (L"CapsuleApp: failed to update capsule - %r\n", Status);
1049 }
1050 }
1051
1052 Status = EFI_SUCCESS;
1053
1054 Done:
1055 for (Index = 0; Index < CapsuleNum; Index++) {
1056 if (CapsuleBuffer[Index] != NULL) {
1057 FreePool (CapsuleBuffer[Index]);
1058 }
1059 }
1060
1061 CleanGatherList(BlockDescriptors, CapsuleNum);
1062
1063 return Status;
1064 }