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