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