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