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