]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - MdeModulePkg/Application/CapsuleApp/CapsuleApp.c
MdeModulePkg/CapsuleApp: Add functions to support Capsule-on-Disk
[mirror_edk2.git] / MdeModulePkg / Application / CapsuleApp / CapsuleApp.c
... / ...
CommitLineData
1/** @file\r
2 A shell application that triggers capsule update process.\r
3\r
4 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include <Uefi.h>\r
16#include <Library/BaseLib.h>\r
17#include <Library/DebugLib.h>\r
18#include <Library/BaseMemoryLib.h>\r
19#include <Library/MemoryAllocationLib.h>\r
20#include <Library/UefiBootServicesTableLib.h>\r
21#include <Library/UefiRuntimeServicesTableLib.h>\r
22#include <Library/UefiLib.h>\r
23#include <Library/PrintLib.h>\r
24#include <Library/BmpSupportLib.h>\r
25#include <Protocol/GraphicsOutput.h>\r
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
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
72DumpCapsuleStatusVariable (\r
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
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
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
116 @retval EFI_NOT_FOUND Shell protocol or file not found\r
117 @retval others Read file failed\r
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
134 @retval EFI_NOT_FOUND Shell protocol not found\r
135 @retval others Write file failed\r
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
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
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
187 Info = Gop->Mode->Info;\r
188 Print(L"Current GOP: Mode - %d, ", Gop->Mode->Mode);\r
189 Print(L"HorizontalResolution - %d, ", Info->HorizontalResolution);\r
190 Print(L"VerticalResolution - %d\n", Info->VerticalResolution);\r
191 // HorizontalResolution >= BMP_IMAGE_HEADER.PixelWidth\r
192 // VerticalResolution >= BMP_IMAGE_HEADER.PixelHeight\r
193\r
194 if (Argc != 5) {\r
195 Print(L"CapsuleApp: Incorrect parameter count.\n");\r
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
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
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
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
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
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
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
443 Print(L"CapsuleApp: Incorrect parameter count.\n");\r
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
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
474\r
475 ImageTypeId = GetCapsuleImageTypeId(CapsuleBuffer);\r
476 if (ImageTypeId == NULL) {\r
477 Print(L"CapsuleApp: Capsule ImageTypeId is not found.\n");\r
478 Status = EFI_INVALID_PARAMETER;\r
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
484 Status = EFI_INVALID_PARAMETER;\r
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
500 NestedCapsuleHeader->Flags = (FwType == ESRT_FW_TYPE_SYSTEMFIRMWARE) ? SYSTEM_FIRMWARE_FLAG : DEVICE_FIRMWARE_FLAG;\r
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
535 BOOLEAN Found;\r
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
541 Found = FALSE;\r
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
552 if (Status == EFI_NOT_FOUND) {\r
553 //\r
554 // There is no more capsule variables, quit\r
555 //\r
556 break;\r
557 }\r
558 Found = TRUE;\r
559\r
560 Print (L"Clear %s %r\n", CapsuleVarName, Status);\r
561\r
562 Index++;\r
563 if (Index > 0xFFFF) {\r
564 break;\r
565 }\r
566 }\r
567\r
568 if (!Found) {\r
569 Print (L"No any Capsule#### variable found\n");\r
570 }\r
571\r
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
630 Print (L"CapsuleApp: capsule data starts at 0x%X with size 0x%X\n", (UINTN) CapsuleBuffer[Index], FileSize[Index]);\r
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
786 TempBlockPtr2 = (VOID *) ((UINTN) TempBlockPtr[Index].Union.ContinuationPointer);\r
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
802 Print(L" CapsuleApp <Capsule...> [-NR]\n");\r
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
810 Print(L" CapsuleApp -P GET <ImageTypeId> <Index> -O <FileName>\n");\r
811 Print(L"Parameter:\n");\r
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
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
817 Print(L" -C: Clear capsule report variable (EFI_CAPSULE_REPORT_GUID),\n");\r
818 Print(L" which is defined in UEFI specification.\n");\r
819 Print(L" -P: Dump UEFI FMP protocol info, or get image with specified\n");\r
820 Print(L" ImageTypeId and Index (decimal format) to a file if 'GET'\n");\r
821 Print(L" option is used.\n");\r
822 Print(L" -E: Dump UEFI ESRT table info.\n");\r
823 Print(L" -G: Convert a BMP file to be an UX capsule,\n");\r
824 Print(L" according to Windows Firmware Update document\n");\r
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
827 Print(L" according to Windows Firmware Update document\n");\r
828 Print(L" -O: Output new Capsule file name\n");\r
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
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
841 @retval EFI_UNSUPPORTED Command usage unsupported.\r
842 @retval EFI_INVALID_PARAMETER Command usage invalid.\r
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
853 RETURN_STATUS RStatus;\r
854 UINTN FileSize[MAX_CAPSULE_NUM];\r
855 VOID *CapsuleBuffer[MAX_CAPSULE_NUM];\r
856 EFI_CAPSULE_BLOCK_DESCRIPTOR *BlockDescriptors;\r
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
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
875 return EFI_UNSUPPORTED;\r
876 }\r
877 if (StrCmp(Argv[1], L"-D") == 0) {\r
878 if (Argc != 3) {\r
879 Print(L"CapsuleApp: Incorrect parameter count.\n");\r
880 return EFI_UNSUPPORTED;\r
881 }\r
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
894 Status = DumpCapsuleStatusVariable();\r
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
902 if (Argc == 2) {\r
903 DumpFmpData();\r
904 }\r
905 if (Argc >= 3) {\r
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
915 //\r
916 // FMP->GetImage()\r
917 //\r
918 RStatus = StrToGuid (Argv[3], &ImageTypeId);\r
919 if (RETURN_ERROR (RStatus) || (Argv[3][GUID_STRING_LENGTH] != L'\0')) {\r
920 Print (L"Invalid ImageTypeId - %s\n", Argv[3]);\r
921 return EFI_INVALID_PARAMETER;\r
922 }\r
923 ImageIndex = StrDecimalToUintn(Argv[4]);\r
924 if (StrCmp(Argv[5], L"-O") != 0) {\r
925 Print(L"CapsuleApp: NO output file name.\n");\r
926 return EFI_UNSUPPORTED;\r
927 }\r
928 DumpFmpImage(&ImageTypeId, ImageIndex, Argv[6]);\r
929 }\r
930 }\r
931 return EFI_SUCCESS;\r
932 }\r
933\r
934 if (StrCmp(Argv[1], L"-E") == 0) {\r
935 DumpEsrtData();\r
936 return EFI_SUCCESS;\r
937 }\r
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
944 CapsuleFirstIndex = 1;\r
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
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
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
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
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
1032 //\r
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
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