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