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