]> git.proxmox.com Git - mirror_edk2.git/blame - SignedCapsulePkg/Universal/RecoveryModuleLoadPei/RecoveryModuleLoadPei.c
SignedCapsulePkg: Add FMP Capsule Image Header extension
[mirror_edk2.git] / SignedCapsulePkg / Universal / RecoveryModuleLoadPei / RecoveryModuleLoadPei.c
CommitLineData
e470ee6d
JY
1/** @file\r
2 Recovery module.\r
3\r
4 Caution: This module requires additional review when modified.\r
5 This module will have external input - capsule image.\r
6 This external input must be validated carefully to avoid security issue like\r
7 buffer overflow, integer overflow.\r
8\r
9 ProcessRecoveryCapsule(), ProcessFmpCapsuleImage(), ProcessRecoveryImage(),\r
10 ValidateFmpCapsule() will receive untrusted input and do basic validation.\r
11\r
868f139b 12Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r
fbf06957 13SPDX-License-Identifier: BSD-2-Clause-Patent\r
e470ee6d
JY
14\r
15**/\r
16\r
17//\r
18// The package level header files this module uses\r
19//\r
20#include <Uefi.h>\r
21#include <PiPei.h>\r
22//\r
c38f0816 23// The protocols, PPI and GUID definitions for this module\r
e470ee6d
JY
24//\r
25#include <Ppi/MasterBootMode.h>\r
26#include <Ppi/BootInRecoveryMode.h>\r
27#include <Ppi/RecoveryModule.h>\r
28#include <Ppi/DeviceRecoveryModule.h>\r
29#include <Ppi/FirmwareVolumeInfo.h>\r
30#include <Guid/FirmwareFileSystem2.h>\r
31#include <Guid/FmpCapsule.h>\r
32#include <Guid/EdkiiSystemFmpCapsule.h>\r
33\r
34//\r
35// The Library classes this module consumes\r
36//\r
37#include <Library/DebugLib.h>\r
38#include <Library/PeimEntryPoint.h>\r
39#include <Library/PeiServicesLib.h>\r
40#include <Library/HobLib.h>\r
41#include <Library/BaseMemoryLib.h>\r
42#include <Library/MemoryAllocationLib.h>\r
43#include <Library/PcdLib.h>\r
44\r
45#include "RecoveryModuleLoadPei.h"\r
46\r
47/**\r
48 Loads a DXE capsule from some media into memory and updates the HOB table\r
49 with the DXE firmware volume information.\r
50\r
51 @param[in] PeiServices General-purpose services that are available to every PEIM.\r
52 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.\r
53\r
54 @retval EFI_SUCCESS The capsule was loaded correctly.\r
55 @retval EFI_DEVICE_ERROR A device error occurred.\r
56 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.\r
57\r
58**/\r
59EFI_STATUS\r
60EFIAPI\r
61LoadRecoveryCapsule (\r
62 IN EFI_PEI_SERVICES **PeiServices,\r
63 IN EFI_PEI_RECOVERY_MODULE_PPI *This\r
64 );\r
65\r
66EFI_PEI_RECOVERY_MODULE_PPI mRecoveryPpi = {\r
67 LoadRecoveryCapsule\r
68};\r
69\r
70EFI_PEI_PPI_DESCRIPTOR mRecoveryPpiList = {\r
71 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
72 &gEfiPeiRecoveryModulePpiGuid,\r
73 &mRecoveryPpi\r
74};\r
75\r
76/**\r
77 Parse Config data file to get the updated data array.\r
78\r
79 @param[in] DataBuffer Config raw file buffer.\r
80 @param[in] BufferSize Size of raw buffer.\r
81 @param[in, out] ConfigHeader Pointer to the config header.\r
82 @param[in, out] RecoveryArray Pointer to the config of recovery data.\r
83\r
84 @retval EFI_NOT_FOUND No config data is found.\r
85 @retval EFI_OUT_OF_RESOURCES No enough memory is allocated.\r
86 @retval EFI_SUCCESS Parse the config file successfully.\r
87\r
88**/\r
89EFI_STATUS\r
90ParseRecoveryDataFile (\r
91 IN UINT8 *DataBuffer,\r
92 IN UINTN BufferSize,\r
93 IN OUT CONFIG_HEADER *ConfigHeader,\r
94 IN OUT RECOVERY_CONFIG_DATA **RecoveryArray\r
95 );\r
96\r
97/**\r
98 Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.\r
99\r
100 @param[in] FmpImageHeader A pointer to EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER\r
101\r
102 @return TRUE It is a system FMP.\r
103 @return FALSE It is a device FMP.\r
104**/\r
105BOOLEAN\r
106IsSystemFmpImage (\r
107 IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *FmpImageHeader\r
108 )\r
109{\r
110 GUID *Guid;\r
111 UINTN Count;\r
112 UINTN Index;\r
113\r
114 Guid = PcdGetPtr(PcdSystemFmpCapsuleImageTypeIdGuid);\r
115 Count = PcdGetSize(PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof(GUID);\r
116\r
117 for (Index = 0; Index < Count; Index++, Guid++) {\r
118 if (CompareGuid(&FmpImageHeader->UpdateImageTypeId, Guid)) {\r
119 return TRUE;\r
120 }\r
121 }\r
122\r
123 return FALSE;\r
124}\r
125\r
126/**\r
127 Return if this CapsuleGuid is a FMP capsule GUID or not.\r
128\r
129 @param[in] CapsuleGuid A pointer to EFI_GUID\r
130\r
131 @return TRUE It is a FMP capsule GUID.\r
132 @return FALSE It is not a FMP capsule GUID.\r
133**/\r
134BOOLEAN\r
135IsFmpCapsuleGuid (\r
136 IN EFI_GUID *CapsuleGuid\r
137 )\r
138{\r
139 if (CompareGuid(&gEfiFmpCapsuleGuid, CapsuleGuid)) {\r
140 return TRUE;\r
141 }\r
142\r
143 return FALSE;\r
144}\r
145\r
146/**\r
c38f0816 147 This function assumes the input Capsule image already passes basic check in\r
e470ee6d
JY
148 ValidateFmpCapsule().\r
149\r
150 Criteria of system FMP capsule is:\r
151 1) FmpCapsuleHeader->EmbeddedDriverCount is 0.\r
152 2) FmpCapsuleHeader->PayloadItemCount is not 0.\r
153 3) All ImageHeader->UpdateImageTypeId matches PcdSystemFmpCapsuleImageTypeIdGuid.\r
154\r
155 @param[in] CapsuleHeader Points to a capsule header.\r
156\r
157 @retval TRUE Input capsule is a correct system FMP capsule.\r
158 @retval FALSE Input capsule is not a correct system FMP capsule.\r
159**/\r
160BOOLEAN\r
161IsSystemFmpCapsuleImage (\r
162 IN EFI_CAPSULE_HEADER *CapsuleHeader\r
163 )\r
164{\r
165 EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
166 EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
167 UINT64 *ItemOffsetList;\r
168 UINT32 ItemNum;\r
169 UINTN Index;\r
170\r
171 FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
172\r
173 if (FmpCapsuleHeader->EmbeddedDriverCount != 0) {\r
174 return FALSE;\r
175 }\r
176\r
177 if (FmpCapsuleHeader->PayloadItemCount == 0) {\r
178 return FALSE;\r
179 }\r
180\r
181 ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;\r
182\r
183 ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
184\r
185 for (Index = 0; Index < ItemNum; Index++) {\r
186 ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);\r
187 if (!IsSystemFmpImage(ImageHeader)) {\r
188 return FALSE;\r
189 }\r
190 }\r
191\r
192 return TRUE;\r
193}\r
194\r
195/**\r
196 Validate if it is valid capsule header\r
197\r
198 This function assumes the caller provided correct CapsuleHeader pointer\r
199 and CapsuleSize.\r
200\r
201 This function validates the fields in EFI_CAPSULE_HEADER.\r
202\r
203 @param[in] CapsuleHeader Points to a capsule header.\r
204 @param[in] CapsuleSize Size of the whole capsule image.\r
205\r
206**/\r
207BOOLEAN\r
208IsValidCapsuleHeader (\r
209 IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
210 IN UINT64 CapsuleSize\r
211 )\r
212{\r
213 if (CapsuleHeader->CapsuleImageSize != CapsuleSize) {\r
214 return FALSE;\r
215 }\r
216 if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) {\r
217 return FALSE;\r
218 }\r
219 return TRUE;\r
220}\r
221\r
222/**\r
223 Validate Fmp capsules layout.\r
224\r
225 Caution: This function may receive untrusted input.\r
226\r
227 This function assumes the caller validated the capsule by using\r
228 IsValidCapsuleHeader(), so that all fields in EFI_CAPSULE_HEADER are correct.\r
229 The capsule buffer size is CapsuleHeader->CapsuleImageSize.\r
230\r
231 This function validates the fields in EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\r
232 and EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.\r
233\r
234 @param[in] CapsuleHeader Points to a capsule header.\r
235 @param[out] IsSystemFmp If it is a system FMP.\r
236 @param[out] EmbeddedDriverCount The EmbeddedDriverCount in the FMP capsule.\r
237\r
c38f0816 238 @retval EFI_SUCCESS Input capsule is a correct FMP capsule.\r
e470ee6d
JY
239 @retval EFI_INVALID_PARAMETER Input capsule is not a correct FMP capsule.\r
240**/\r
241EFI_STATUS\r
242ValidateFmpCapsule (\r
243 IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
244 OUT BOOLEAN *IsSystemFmp, OPTIONAL\r
245 OUT UINT16 *EmbeddedDriverCount OPTIONAL\r
246 )\r
247{\r
248 EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
249 UINT8 *EndOfCapsule;\r
250 EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
251 UINT8 *EndOfPayload;\r
252 UINT64 *ItemOffsetList;\r
253 UINT32 ItemNum;\r
254 UINTN Index;\r
255 UINTN FmpCapsuleSize;\r
256 UINTN FmpCapsuleHeaderSize;\r
257 UINT64 FmpImageSize;\r
258 UINTN FmpImageHeaderSize;\r
259\r
260 if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) {\r
261 DEBUG((DEBUG_ERROR, "HeaderSize(0x%x) >= CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize));\r
262 return EFI_INVALID_PARAMETER;\r
263 }\r
264\r
265 FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
266 EndOfCapsule = (UINT8 *) CapsuleHeader + CapsuleHeader->CapsuleImageSize;\r
267 FmpCapsuleSize = (UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader;\r
268\r
269 if (FmpCapsuleSize < sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER)) {\r
270 DEBUG((DEBUG_ERROR, "FmpCapsuleSize(0x%x) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\n", FmpCapsuleSize));\r
271 return EFI_INVALID_PARAMETER;\r
272 }\r
273\r
274 // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\r
275 if (FmpCapsuleHeader->Version != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {\r
276 DEBUG((DEBUG_ERROR, "FmpCapsuleHeader->Version(0x%x) != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION\n", FmpCapsuleHeader->Version));\r
277 return EFI_INVALID_PARAMETER;\r
278 }\r
279 ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
280\r
281 // No overflow\r
282 ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;\r
283\r
284 if ((FmpCapsuleSize - sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER))/sizeof(UINT64) < ItemNum) {\r
285 DEBUG((DEBUG_ERROR, "ItemNum(0x%x) too big\n", ItemNum));\r
286 return EFI_INVALID_PARAMETER;\r
287 }\r
288 FmpCapsuleHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER) + sizeof(UINT64)*ItemNum;\r
289\r
290 // Check ItemOffsetList\r
291 for (Index = 0; Index < ItemNum; Index++) {\r
292 if (ItemOffsetList[Index] >= FmpCapsuleSize) {\r
293 DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) >= FmpCapsuleSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleSize));\r
294 return EFI_INVALID_PARAMETER;\r
295 }\r
296 if (ItemOffsetList[Index] < FmpCapsuleHeaderSize) {\r
297 DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < FmpCapsuleHeaderSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleHeaderSize));\r
298 return EFI_INVALID_PARAMETER;\r
299 }\r
300 //\r
301 // All the address in ItemOffsetList must be stored in ascending order\r
302 //\r
303 if (Index > 0) {\r
304 if (ItemOffsetList[Index] <= ItemOffsetList[Index - 1]) {\r
305 DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < ItemOffsetList[%d](0x%x)\n", Index, ItemOffsetList[Index], Index, ItemOffsetList[Index - 1]));\r
306 return EFI_INVALID_PARAMETER;\r
307 }\r
308 }\r
309 }\r
310\r
311 // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER\r
312 for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) {\r
313 ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);\r
314 if (Index == ItemNum - 1) {\r
315 EndOfPayload = (UINT8 *)((UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader);\r
316 } else {\r
317 EndOfPayload = (UINT8 *)(UINTN)ItemOffsetList[Index+1];\r
318 }\r
319 FmpImageSize = (UINTN)EndOfPayload - ItemOffsetList[Index];\r
320\r
321 if (FmpImageSize < OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance)) {\r
322 DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER\n", FmpImageSize));\r
323 return EFI_INVALID_PARAMETER;\r
324 }\r
325 FmpImageHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER);\r
326 if ((ImageHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) ||\r
327 (ImageHeader->Version < 1)) {\r
328 DEBUG((DEBUG_ERROR, "ImageHeader->Version(0x%x) Unknown\n", ImageHeader->Version));\r
329 return EFI_INVALID_PARAMETER;\r
330 }\r
bc5012b8
OY
331 ///\r
332 /// Current Init ImageHeader version is 3. UpdateHardwareInstance field was added in version 2\r
333 /// and ImageCapsuleSupport field was added in version 3\r
334 ///\r
335 if (ImageHeader->Version == 1) {\r
e470ee6d 336 FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);\r
bc5012b8
OY
337 } else if (ImageHeader->Version == 2){\r
338 FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, ImageCapsuleSupport);\r
e470ee6d
JY
339 }\r
340\r
341 // No overflow\r
342 if (FmpImageSize != (UINT64)FmpImageHeaderSize + (UINT64)ImageHeader->UpdateImageSize + (UINT64)ImageHeader->UpdateVendorCodeSize) {\r
343 DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) mismatch, UpdateImageSize(0x%x) UpdateVendorCodeSize(0x%x)\n", FmpImageSize, ImageHeader->UpdateImageSize, ImageHeader->UpdateVendorCodeSize));\r
344 return EFI_INVALID_PARAMETER;\r
345 }\r
346 }\r
347\r
348 if (ItemNum == 0) {\r
349 //\r
350 // No driver & payload element in FMP\r
351 //\r
352 EndOfPayload = (UINT8 *)(FmpCapsuleHeader + 1);\r
353 if (EndOfPayload != EndOfCapsule) {\r
354 DEBUG((DEBUG_ERROR, "EndOfPayload(0x%x) mismatch, EndOfCapsule(0x%x)\n", EndOfPayload, EndOfCapsule));\r
355 return EFI_INVALID_PARAMETER;\r
356 }\r
357 return EFI_UNSUPPORTED;\r
358 }\r
359\r
360 //\r
361 // Check in system FMP capsule\r
362 //\r
363 if (IsSystemFmp != NULL) {\r
364 *IsSystemFmp = IsSystemFmpCapsuleImage(CapsuleHeader);\r
365 }\r
366\r
367 if (EmbeddedDriverCount != NULL) {\r
368 *EmbeddedDriverCount = FmpCapsuleHeader->EmbeddedDriverCount;\r
369 }\r
370\r
371 return EFI_SUCCESS;\r
372}\r
373\r
374/**\r
375 Recovery module entrypoint\r
376\r
377 @param[in] FileHandle Handle of the file being invoked.\r
378 @param[in] PeiServices Describes the list of possible PEI Services.\r
379\r
380 @return EFI_SUCCESS Recovery module is initialized.\r
381**/\r
382EFI_STATUS\r
383EFIAPI\r
384InitializeRecoveryModule (\r
385 IN EFI_PEI_FILE_HANDLE FileHandle,\r
386 IN CONST EFI_PEI_SERVICES **PeiServices\r
387 )\r
388{\r
389 EFI_STATUS Status;\r
390 UINTN BootMode;\r
391\r
392 BootMode = GetBootModeHob();\r
393 ASSERT(BootMode == BOOT_IN_RECOVERY_MODE);\r
394\r
395 Status = (**PeiServices).InstallPpi (PeiServices, &mRecoveryPpiList);\r
396 ASSERT_EFI_ERROR (Status);\r
397\r
398 return Status;\r
399}\r
400\r
401/**\r
402 Create hob and install FvInfo PPI for recovery capsule.\r
403\r
404 @param[in] FvImage Points to the DXE FV image.\r
405 @param[in] FvImageSize The length of the DXE FV image in bytes.\r
406\r
c38f0816 407 @retval EFI_SUCCESS Create hob and install FvInfo PPI successfully.\r
e470ee6d
JY
408 @retval EFI_VOLUME_CORRUPTED The input data is not an FV.\r
409 @retval EFI_OUT_OF_RESOURCES No enough resource to process the input data.\r
410**/\r
411EFI_STATUS\r
412EFIAPI\r
413CreateHobForRecoveryCapsule (\r
414 IN VOID *FvImage,\r
415 IN UINTN FvImageSize\r
416 )\r
417{\r
418 EFI_FIRMWARE_VOLUME_HEADER *FvHeader;\r
419 UINT32 FvAlignment;\r
420 UINT64 FvLength;\r
421 VOID *NewFvBuffer;\r
422\r
423 //\r
424 // FvImage should be at its required alignment.\r
425 //\r
426 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) FvImage;\r
427 //\r
428 // Validate FV Header, if not as expected, return\r
429 //\r
430 if (ReadUnaligned32 (&FvHeader->Signature) != EFI_FVH_SIGNATURE) {\r
431 DEBUG((DEBUG_ERROR, "CreateHobForRecoveryCapsule (Fv Signature Error)\n"));\r
432 return EFI_VOLUME_CORRUPTED;\r
433 }\r
434 //\r
435 // If EFI_FVB2_WEAK_ALIGNMENT is set in the volume header then the first byte of the volume\r
436 // can be aligned on any power-of-two boundary. A weakly aligned volume can not be moved from\r
437 // its initial linked location and maintain its alignment.\r
438 //\r
439 if ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_WEAK_ALIGNMENT) != EFI_FVB2_WEAK_ALIGNMENT) {\r
440 //\r
441 // Get FvHeader alignment\r
442 //\r
443 FvAlignment = 1 << ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_ALIGNMENT) >> 16);\r
444 //\r
445 // FvAlignment must be greater than or equal to 8 bytes of the minimum FFS alignment value.\r
446 //\r
447 if (FvAlignment < 8) {\r
448 FvAlignment = 8;\r
449 }\r
450 //\r
451 // Allocate the aligned buffer for the FvImage.\r
452 //\r
453 if ((UINTN) FvHeader % FvAlignment != 0) {\r
454 DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule (FvHeader 0x%lx is not aligned)\n", (UINT64)(UINTN)FvHeader));\r
455 FvLength = ReadUnaligned64 (&FvHeader->FvLength);\r
456 NewFvBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINTN) FvLength), FvAlignment);\r
457 if (NewFvBuffer == NULL) {\r
458 DEBUG((DEBUG_ERROR, "CreateHobForRecoveryCapsule (Not enough resource to allocate 0x%lx bytes)\n", FvLength));\r
459 return EFI_OUT_OF_RESOURCES;\r
460 }\r
461 CopyMem (NewFvBuffer, FvHeader, (UINTN) FvLength);\r
462 FvHeader = (EFI_FIRMWARE_VOLUME_HEADER*) NewFvBuffer;\r
463 }\r
464 }\r
465\r
466 BuildFvHob((UINT64)(UINTN)FvHeader, FvHeader->FvLength);\r
467 DEBUG((DEBUG_INFO, "BuildFvHob (FV in recovery) - 0x%lx - 0x%lx\n", (UINT64)(UINTN)FvHeader, FvHeader->FvLength));\r
468\r
469 PeiServicesInstallFvInfoPpi(\r
2ed235fc 470 &FvHeader->FileSystemGuid,\r
e470ee6d
JY
471 (VOID *)FvHeader,\r
472 (UINT32)FvHeader->FvLength,\r
473 NULL,\r
474 NULL\r
475 );\r
476\r
477 return EFI_SUCCESS;\r
478}\r
479\r
480/**\r
481 Create recovery context based upon System Firmware image and config file.\r
482\r
483 @param[in] SystemFirmwareImage Points to the System Firmware image.\r
484 @param[in] SystemFirmwareImageSize The length of the System Firmware image in bytes.\r
485 @param[in] ConfigImage Points to the config file image.\r
486 @param[in] ConfigImageSize The length of the config file image in bytes.\r
487\r
c38f0816 488 @retval EFI_SUCCESS Process Recovery Image successfully.\r
e470ee6d
JY
489**/\r
490EFI_STATUS\r
491RecoverImage (\r
492 IN VOID *SystemFirmwareImage,\r
493 IN UINTN SystemFirmwareImageSize,\r
494 IN VOID *ConfigImage,\r
495 IN UINTN ConfigImageSize\r
496 )\r
497{\r
498 EFI_STATUS Status;\r
499 RECOVERY_CONFIG_DATA *ConfigData;\r
500 RECOVERY_CONFIG_DATA *RecoveryConfigData;\r
501 CONFIG_HEADER ConfigHeader;\r
502 UINTN Index;\r
503\r
504 if (ConfigImage == NULL) {\r
505 DEBUG((DEBUG_INFO, "RecoverImage (NoConfig)\n"));\r
506 Status = CreateHobForRecoveryCapsule(\r
507 SystemFirmwareImage,\r
508 SystemFirmwareImageSize\r
509 );\r
510 return Status;\r
511 }\r
512\r
513 ConfigData = NULL;\r
514 ZeroMem (&ConfigHeader, sizeof(ConfigHeader));\r
515 Status = ParseRecoveryDataFile (\r
516 ConfigImage,\r
517 ConfigImageSize,\r
518 &ConfigHeader,\r
519 &ConfigData\r
520 );\r
521 DEBUG((DEBUG_INFO, "ParseRecoveryDataFile - %r\n", Status));\r
522 if (EFI_ERROR(Status)) {\r
523 return Status;\r
524 }\r
525 DEBUG((DEBUG_INFO, "ConfigHeader.NumOfRecovery - 0x%x\n", ConfigHeader.NumOfRecovery));\r
526 DEBUG((DEBUG_INFO, "PcdEdkiiSystemFirmwareFileGuid - %g\n", PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid)));\r
527\r
528 Index = 0;\r
529 RecoveryConfigData = ConfigData;\r
530 while (Index < ConfigHeader.NumOfRecovery) {\r
531 if (CompareGuid(&RecoveryConfigData->FileGuid, PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid))) {\r
532 DEBUG((DEBUG_INFO, "FileGuid - %g (processing)\n", &RecoveryConfigData->FileGuid));\r
533 Status = CreateHobForRecoveryCapsule (\r
534 (UINT8 *)SystemFirmwareImage + RecoveryConfigData->ImageOffset,\r
535 RecoveryConfigData->Length\r
536 );\r
537 //\r
538 // Shall updates be serialized so that if a recovery FV is not successfully completed,\r
539 // the remaining updates won't be performed.\r
540 //\r
541 if (EFI_ERROR (Status)) {\r
542 break;\r
543 }\r
544 } else {\r
545 DEBUG((DEBUG_INFO, "FileGuid - %g (ignored)\n", &RecoveryConfigData->FileGuid));\r
546 }\r
547\r
548 Index++;\r
549 RecoveryConfigData++;\r
550 }\r
551\r
552 return Status;\r
553}\r
554\r
555/**\r
556 Process recovery image.\r
557\r
558 Caution: This function may receive untrusted input.\r
559\r
560 @param[in] Image Points to the recovery image.\r
561 @param[in] Length The length of the recovery image in bytes.\r
562\r
c38f0816 563 @retval EFI_SUCCESS Process Recovery Image successfully.\r
e470ee6d
JY
564 @retval EFI_SECURITY_VIOLATION Recovery image is not processed due to security violation.\r
565**/\r
566EFI_STATUS\r
567ProcessRecoveryImage (\r
568 IN VOID *Image,\r
569 IN UINTN Length\r
570 )\r
571{\r
572 UINT32 LastAttemptVersion;\r
573 UINT32 LastAttemptStatus;\r
574 EFI_STATUS Status;\r
575 VOID *SystemFirmwareImage;\r
576 UINTN SystemFirmwareImageSize;\r
577 VOID *ConfigImage;\r
578 UINTN ConfigImageSize;\r
579 VOID *AuthenticatedImage;\r
580 UINTN AuthenticatedImageSize;\r
581\r
8b66342c
HW
582 AuthenticatedImage = NULL;\r
583 AuthenticatedImageSize = 0;\r
584\r
e470ee6d
JY
585 Status = CapsuleAuthenticateSystemFirmware(Image, Length, TRUE, &LastAttemptVersion, &LastAttemptStatus, &AuthenticatedImage, &AuthenticatedImageSize);\r
586 if (EFI_ERROR(Status)) {\r
587 DEBUG((DEBUG_INFO, "CapsuleAuthenticateSystemFirmware - %r\n", Status));\r
588 return Status;\r
589 }\r
590\r
591 ExtractSystemFirmwareImage(AuthenticatedImage, AuthenticatedImageSize, &SystemFirmwareImage, &SystemFirmwareImageSize);\r
592 ExtractConfigImage(AuthenticatedImage, AuthenticatedImageSize, &ConfigImage, &ConfigImageSize);\r
593\r
594 Status = RecoverImage(SystemFirmwareImage, SystemFirmwareImageSize, ConfigImage, ConfigImageSize);\r
595 if (EFI_ERROR(Status)) {\r
596 DEBUG((DEBUG_INFO, "RecoverImage - %r\n", Status));\r
597 return Status;\r
598 }\r
599\r
600 return EFI_SUCCESS;\r
601}\r
602\r
603/**\r
604 Process Firmware management protocol data capsule.\r
605\r
606 Caution: This function may receive untrusted input.\r
607\r
608 This function assumes the caller validated the capsule by using\r
609 ValidateFmpCapsule(), so that all fields in EFI_CAPSULE_HEADER,\r
610 EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and\r
611 EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER are correct.\r
612\r
613 @param[in] CapsuleHeader Points to a capsule header.\r
614 @param[in] IsSystemFmp If this capsule is a system FMP capsule.\r
615\r
c38f0816 616 @retval EFI_SUCCESS Process Capsule Image successfully.\r
e470ee6d
JY
617 @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware.\r
618 @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted.\r
619 @retval EFI_OUT_OF_RESOURCES Not enough memory.\r
620**/\r
621EFI_STATUS\r
622ProcessFmpCapsuleImage (\r
623 IN EFI_CAPSULE_HEADER *CapsuleHeader,\r
624 IN BOOLEAN IsSystemFmp\r
625 )\r
626{\r
627 EFI_STATUS Status;\r
628 EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
629 EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
630 UINT8 *Image;\r
631 UINT64 *ItemOffsetList;\r
632 UINTN ItemIndex;\r
633\r
634 if (!IsSystemFmp) {\r
635 return EFI_UNSUPPORTED;\r
636 }\r
637\r
638 FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize);\r
639 ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
640\r
641 for (ItemIndex = 0; ItemIndex < FmpCapsuleHeader->PayloadItemCount; ItemIndex++) {\r
642 ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemIndex]);\r
643 if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {\r
644 Image = (UINT8 *)(ImageHeader + 1);\r
645 } else {\r
646 //\r
647 // If the EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is version 1, only match ImageTypeId.\r
bc5012b8
OY
648 // Header should exclude UpdateHardwareInstance field.\r
649 // If version is 2 Header should exclude ImageCapsuleSupport field.\r
e470ee6d 650 //\r
bc5012b8
OY
651 if (ImageHeader->Version == 1) {\r
652 Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);\r
653 } else {\r
654 Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, ImageCapsuleSupport);\r
655 }\r
e470ee6d
JY
656 }\r
657\r
658 Status = ProcessRecoveryImage (Image, ImageHeader->UpdateImageSize);\r
659 if (EFI_ERROR(Status)) {\r
660 return Status;\r
661 }\r
662 }\r
663\r
664 return EFI_SUCCESS;\r
665}\r
666\r
667/**\r
668 Process recovery capsule image.\r
669\r
670 Caution: This function may receive untrusted input.\r
671\r
672 @param[in] CapsuleBuffer The capsule image buffer.\r
673 @param[in] CapsuleSize The size of the capsule image in bytes.\r
674\r
675 @retval EFI_SUCCESS The recovery capsule is processed.\r
676 @retval EFI_SECURITY_VIOLATION The recovery capsule is not process because of security violation.\r
677 @retval EFI_NOT_FOUND The recovery capsule is not process because of unrecognization.\r
678**/\r
679EFI_STATUS\r
680EFIAPI\r
681ProcessRecoveryCapsule (\r
682 IN VOID *CapsuleBuffer,\r
683 IN UINTN CapsuleSize\r
684 )\r
685{\r
686 EFI_STATUS Status;\r
687 BOOLEAN IsSystemFmp;\r
688 EFI_CAPSULE_HEADER *CapsuleHeader;\r
689\r
690 CapsuleHeader = CapsuleBuffer;\r
691 if (!IsValidCapsuleHeader (CapsuleHeader, CapsuleSize)) {\r
692 DEBUG((DEBUG_ERROR, "CapsuleImageSize incorrect\n"));\r
693 return EFI_SECURITY_VIOLATION;\r
694 }\r
695\r
696 //\r
697 // Check FMP capsule layout\r
698 //\r
699 if (IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) {\r
700 DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule\n"));\r
701\r
702 DEBUG((DEBUG_INFO, "ProcessCapsuleImage for FmpCapsule ...\n"));\r
703 DEBUG((DEBUG_INFO, "ValidateFmpCapsule ...\n"));\r
704 Status = ValidateFmpCapsule(CapsuleHeader, &IsSystemFmp, NULL);\r
705 DEBUG((DEBUG_INFO, "ValidateFmpCapsule - %r\n", Status));\r
706 if (EFI_ERROR(Status)) {\r
707 return Status;\r
708 }\r
709\r
710 //\r
70bd2a85 711 // Process EFI FMP Capsule\r
e470ee6d
JY
712 //\r
713 DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n"));\r
714 Status = ProcessFmpCapsuleImage(CapsuleHeader, IsSystemFmp);\r
715 DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status));\r
716\r
717 DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule Done\n"));\r
718 return Status;\r
719 }\r
720\r
721 return EFI_UNSUPPORTED;\r
722}\r
723\r
724/**\r
725 Loads a DXE capsule from some media into memory and updates the HOB table\r
726 with the DXE firmware volume information.\r
727\r
728 @param[in] PeiServices General-purpose services that are available to every PEIM.\r
729 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.\r
730\r
731 @retval EFI_SUCCESS The capsule was loaded correctly.\r
732 @retval EFI_DEVICE_ERROR A device error occurred.\r
733 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.\r
734\r
735**/\r
736EFI_STATUS\r
737EFIAPI\r
738LoadRecoveryCapsule (\r
739 IN EFI_PEI_SERVICES **PeiServices,\r
740 IN EFI_PEI_RECOVERY_MODULE_PPI *This\r
741 )\r
742{\r
743 EFI_STATUS Status;\r
744 EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryPpi;\r
745 UINTN NumberRecoveryCapsules;\r
746 UINTN Instance;\r
747 UINTN CapsuleInstance;\r
748 UINTN CapsuleSize;\r
749 EFI_GUID CapsuleType;\r
750 VOID *CapsuleBuffer;\r
751\r
752 DEBUG((DEBUG_INFO | DEBUG_LOAD, "Recovery Entry\n"));\r
753\r
754 for (Instance = 0; ; Instance++) {\r
755 Status = PeiServicesLocatePpi (\r
756 &gEfiPeiDeviceRecoveryModulePpiGuid,\r
757 Instance,\r
758 NULL,\r
759 (VOID **)&DeviceRecoveryPpi\r
760 );\r
761 DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LocateRecoveryPpi (%d) - %r\n", Instance, Status));\r
762 if (EFI_ERROR (Status)) {\r
763 break;\r
764 }\r
765 NumberRecoveryCapsules = 0;\r
766 Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules (\r
767 (EFI_PEI_SERVICES **)PeiServices,\r
768 DeviceRecoveryPpi,\r
769 &NumberRecoveryCapsules\r
770 );\r
771 DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status));\r
772 if (EFI_ERROR (Status)) {\r
773 continue;\r
774 }\r
775 for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) {\r
776 CapsuleSize = 0;\r
777 Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo (\r
778 (EFI_PEI_SERVICES **)PeiServices,\r
779 DeviceRecoveryPpi,\r
868f139b 780 CapsuleInstance,\r
e470ee6d
JY
781 &CapsuleSize,\r
782 &CapsuleType\r
783 );\r
784 DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status));\r
785 if (EFI_ERROR (Status)) {\r
786 break;\r
787 }\r
788\r
789 CapsuleBuffer = AllocatePages (EFI_SIZE_TO_PAGES(CapsuleSize));\r
790 if (CapsuleBuffer == NULL) {\r
791 DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - AllocatePool fail\n"));\r
792 continue;\r
793 }\r
794 Status = DeviceRecoveryPpi->LoadRecoveryCapsule (\r
795 (EFI_PEI_SERVICES **)PeiServices,\r
796 DeviceRecoveryPpi,\r
868f139b 797 CapsuleInstance,\r
e470ee6d
JY
798 CapsuleBuffer\r
799 );\r
800 DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status));\r
801 if (EFI_ERROR (Status)) {\r
802 FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize));\r
803 break;\r
804 }\r
805 //\r
806 // good, load capsule buffer\r
807 //\r
808 Status = ProcessRecoveryCapsule (CapsuleBuffer, CapsuleSize);\r
809 return Status;\r
810 }\r
811 }\r
812\r
813 return EFI_NOT_FOUND;\r
814}\r