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