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
12Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
13This program and the accompanying materials
14are licensed and made available under the terms and conditions of the BSD License
15which accompanies this distribution.  The full text of the license may be found at
16http://opensource.org/licenses/bsd-license.php
17
18THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
19WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20
21**/
22
23//
24// The package level header files this module uses
25//
26#include <Uefi.h>
27#include <PiPei.h>
28//
29// The protocols, PPI and GUID defintions for this module
30//
31#include <Ppi/MasterBootMode.h>
32#include <Ppi/BootInRecoveryMode.h>
33#include <Ppi/RecoveryModule.h>
34#include <Ppi/DeviceRecoveryModule.h>
35#include <Ppi/FirmwareVolumeInfo.h>
36#include <Guid/FirmwareFileSystem2.h>
37#include <Guid/FmpCapsule.h>
38#include <Guid/EdkiiSystemFmpCapsule.h>
39
40//
41// The Library classes this module consumes
42//
43#include <Library/DebugLib.h>
44#include <Library/PeimEntryPoint.h>
45#include <Library/PeiServicesLib.h>
46#include <Library/HobLib.h>
47#include <Library/BaseMemoryLib.h>
48#include <Library/MemoryAllocationLib.h>
49#include <Library/PcdLib.h>
50
51#include "RecoveryModuleLoadPei.h"
52
53/**
54  Loads a DXE capsule from some media into memory and updates the HOB table
55  with the DXE firmware volume information.
56
57  @param[in]  PeiServices   General-purpose services that are available to every PEIM.
58  @param[in]  This          Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.
59
60  @retval EFI_SUCCESS        The capsule was loaded correctly.
61  @retval EFI_DEVICE_ERROR   A device error occurred.
62  @retval EFI_NOT_FOUND      A recovery DXE capsule cannot be found.
63
64**/
65EFI_STATUS
66EFIAPI
67LoadRecoveryCapsule (
68  IN EFI_PEI_SERVICES                     **PeiServices,
69  IN EFI_PEI_RECOVERY_MODULE_PPI          *This
70  );
71
72EFI_PEI_RECOVERY_MODULE_PPI mRecoveryPpi = {
73  LoadRecoveryCapsule
74};
75
76EFI_PEI_PPI_DESCRIPTOR mRecoveryPpiList = {
77  (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
78  &gEfiPeiRecoveryModulePpiGuid,
79  &mRecoveryPpi
80};
81
82/**
83  Parse Config data file to get the updated data array.
84
85  @param[in]      DataBuffer      Config raw file buffer.
86  @param[in]      BufferSize      Size of raw buffer.
87  @param[in, out] ConfigHeader    Pointer to the config header.
88  @param[in, out] RecoveryArray   Pointer to the config of recovery data.
89
90  @retval EFI_NOT_FOUND         No config data is found.
91  @retval EFI_OUT_OF_RESOURCES  No enough memory is allocated.
92  @retval EFI_SUCCESS           Parse the config file successfully.
93
94**/
95EFI_STATUS
96ParseRecoveryDataFile (
97  IN      UINT8                         *DataBuffer,
98  IN      UINTN                         BufferSize,
99  IN OUT  CONFIG_HEADER                 *ConfigHeader,
100  IN OUT  RECOVERY_CONFIG_DATA          **RecoveryArray
101  );
102
103/**
104  Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.
105
106  @param[in] FmpImageHeader A pointer to EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER
107
108  @return TRUE  It is a system FMP.
109  @return FALSE It is a device FMP.
110**/
111BOOLEAN
112IsSystemFmpImage (
113  IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER   *FmpImageHeader
114  )
115{
116  GUID      *Guid;
117  UINTN     Count;
118  UINTN     Index;
119
120  Guid = PcdGetPtr(PcdSystemFmpCapsuleImageTypeIdGuid);
121  Count = PcdGetSize(PcdSystemFmpCapsuleImageTypeIdGuid) / sizeof(GUID);
122
123  for (Index = 0; Index < Count; Index++, Guid++) {
124    if (CompareGuid(&FmpImageHeader->UpdateImageTypeId, Guid)) {
125      return TRUE;
126    }
127  }
128
129  return FALSE;
130}
131
132/**
133  Return if this CapsuleGuid is a FMP capsule GUID or not.
134
135  @param[in] CapsuleGuid A pointer to EFI_GUID
136
137  @return TRUE  It is a FMP capsule GUID.
138  @return FALSE It is not a FMP capsule GUID.
139**/
140BOOLEAN
141IsFmpCapsuleGuid (
142  IN EFI_GUID  *CapsuleGuid
143  )
144{
145  if (CompareGuid(&gEfiFmpCapsuleGuid, CapsuleGuid)) {
146    return TRUE;
147  }
148
149  return FALSE;
150}
151
152/**
153  This function assumes the input Capusule image already passes basic check in
154  ValidateFmpCapsule().
155
156  Criteria of system FMP capsule is:
157  1) FmpCapsuleHeader->EmbeddedDriverCount is 0.
158  2) FmpCapsuleHeader->PayloadItemCount is not 0.
159  3) All ImageHeader->UpdateImageTypeId matches PcdSystemFmpCapsuleImageTypeIdGuid.
160
161  @param[in]  CapsuleHeader    Points to a capsule header.
162
163  @retval TRUE   Input capsule is a correct system FMP capsule.
164  @retval FALSE  Input capsule is not a correct system FMP capsule.
165**/
166BOOLEAN
167IsSystemFmpCapsuleImage (
168  IN EFI_CAPSULE_HEADER *CapsuleHeader
169  )
170{
171  EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER       *FmpCapsuleHeader;
172  EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;
173  UINT64                                       *ItemOffsetList;
174  UINT32                                       ItemNum;
175  UINTN                                        Index;
176
177  FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);
178
179  if (FmpCapsuleHeader->EmbeddedDriverCount != 0) {
180    return FALSE;
181  }
182
183  if (FmpCapsuleHeader->PayloadItemCount == 0) {
184    return FALSE;
185  }
186
187  ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;
188
189  ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);
190
191  for (Index = 0; Index < ItemNum; Index++) {
192    ImageHeader  = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);
193    if (!IsSystemFmpImage(ImageHeader)) {
194      return FALSE;
195    }
196  }
197
198  return TRUE;
199}
200
201/**
202  Validate if it is valid capsule header
203
204  This function assumes the caller provided correct CapsuleHeader pointer
205  and CapsuleSize.
206
207  This function validates the fields in EFI_CAPSULE_HEADER.
208
209  @param[in]  CapsuleHeader    Points to a capsule header.
210  @param[in]  CapsuleSize      Size of the whole capsule image.
211
212**/
213BOOLEAN
214IsValidCapsuleHeader (
215  IN EFI_CAPSULE_HEADER  *CapsuleHeader,
216  IN UINT64              CapsuleSize
217  )
218{
219  if (CapsuleHeader->CapsuleImageSize != CapsuleSize) {
220    return FALSE;
221  }
222  if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) {
223    return FALSE;
224  }
225  return TRUE;
226}
227
228/**
229  Validate Fmp capsules layout.
230
231  Caution: This function may receive untrusted input.
232
233  This function assumes the caller validated the capsule by using
234  IsValidCapsuleHeader(), so that all fields in EFI_CAPSULE_HEADER are correct.
235  The capsule buffer size is CapsuleHeader->CapsuleImageSize.
236
237  This function validates the fields in EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
238  and EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.
239
240  @param[in]   CapsuleHeader        Points to a capsule header.
241  @param[out]  IsSystemFmp          If it is a system FMP.
242  @param[out]  EmbeddedDriverCount  The EmbeddedDriverCount in the FMP capsule.
243
244  @retval EFI_SUCESS             Input capsule is a correct FMP capsule.
245  @retval EFI_INVALID_PARAMETER  Input capsule is not a correct FMP capsule.
246**/
247EFI_STATUS
248ValidateFmpCapsule (
249  IN EFI_CAPSULE_HEADER *CapsuleHeader,
250  OUT BOOLEAN           *IsSystemFmp, OPTIONAL
251  OUT UINT16            *EmbeddedDriverCount OPTIONAL
252  )
253{
254  EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER       *FmpCapsuleHeader;
255  UINT8                                        *EndOfCapsule;
256  EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;
257  UINT8                                        *EndOfPayload;
258  UINT64                                       *ItemOffsetList;
259  UINT32                                       ItemNum;
260  UINTN                                        Index;
261  UINTN                                        FmpCapsuleSize;
262  UINTN                                        FmpCapsuleHeaderSize;
263  UINT64                                       FmpImageSize;
264  UINTN                                        FmpImageHeaderSize;
265
266  if (CapsuleHeader->HeaderSize >= CapsuleHeader->CapsuleImageSize) {
267    DEBUG((DEBUG_ERROR, "HeaderSize(0x%x) >= CapsuleImageSize(0x%x)\n", CapsuleHeader->HeaderSize, CapsuleHeader->CapsuleImageSize));
268    return EFI_INVALID_PARAMETER;
269  }
270
271  FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);
272  EndOfCapsule     = (UINT8 *) CapsuleHeader + CapsuleHeader->CapsuleImageSize;
273  FmpCapsuleSize   = (UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader;
274
275  if (FmpCapsuleSize < sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER)) {
276    DEBUG((DEBUG_ERROR, "FmpCapsuleSize(0x%x) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER\n", FmpCapsuleSize));
277    return EFI_INVALID_PARAMETER;
278  }
279
280  // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
281  if (FmpCapsuleHeader->Version != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {
282    DEBUG((DEBUG_ERROR, "FmpCapsuleHeader->Version(0x%x) != EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION\n", FmpCapsuleHeader->Version));
283    return EFI_INVALID_PARAMETER;
284  }
285  ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);
286
287  // No overflow
288  ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;
289
290  if ((FmpCapsuleSize - sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER))/sizeof(UINT64) < ItemNum) {
291    DEBUG((DEBUG_ERROR, "ItemNum(0x%x) too big\n", ItemNum));
292    return EFI_INVALID_PARAMETER;
293  }
294  FmpCapsuleHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER) + sizeof(UINT64)*ItemNum;
295
296  // Check ItemOffsetList
297  for (Index = 0; Index < ItemNum; Index++) {
298    if (ItemOffsetList[Index] >= FmpCapsuleSize) {
299      DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) >= FmpCapsuleSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleSize));
300      return EFI_INVALID_PARAMETER;
301    }
302    if (ItemOffsetList[Index] < FmpCapsuleHeaderSize) {
303      DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < FmpCapsuleHeaderSize(0x%x)\n", Index, ItemOffsetList[Index], FmpCapsuleHeaderSize));
304      return EFI_INVALID_PARAMETER;
305    }
306    //
307    // All the address in ItemOffsetList must be stored in ascending order
308    //
309    if (Index > 0) {
310      if (ItemOffsetList[Index] <= ItemOffsetList[Index - 1]) {
311        DEBUG((DEBUG_ERROR, "ItemOffsetList[%d](0x%lx) < ItemOffsetList[%d](0x%x)\n", Index, ItemOffsetList[Index], Index, ItemOffsetList[Index - 1]));
312        return EFI_INVALID_PARAMETER;
313      }
314    }
315  }
316
317  // Check EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER
318  for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) {
319    ImageHeader  = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);
320    if (Index == ItemNum - 1) {
321      EndOfPayload = (UINT8 *)((UINTN)EndOfCapsule - (UINTN)FmpCapsuleHeader);
322    } else {
323      EndOfPayload = (UINT8 *)(UINTN)ItemOffsetList[Index+1];
324    }
325    FmpImageSize = (UINTN)EndOfPayload - ItemOffsetList[Index];
326
327    if (FmpImageSize < OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance)) {
328      DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) < EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER\n", FmpImageSize));
329      return EFI_INVALID_PARAMETER;
330    }
331    FmpImageHeaderSize = sizeof(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER);
332    if ((ImageHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) ||
333        (ImageHeader->Version < 1)) {
334      DEBUG((DEBUG_ERROR, "ImageHeader->Version(0x%x) Unknown\n", ImageHeader->Version));
335      return EFI_INVALID_PARAMETER;
336    }
337    if (ImageHeader->Version < EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {
338      FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);
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**/
382EFI_STATUS
383EFIAPI
384InitializeRecoveryModule (
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_SUCESS            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**/
411EFI_STATUS
412EFIAPI
413CreateHobForRecoveryCapsule (
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    NULL,
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_SUCESS             Process Recovery Image successfully.
489**/
490EFI_STATUS
491RecoverImage (
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_SUCESS             Process Recovery Image successfully.
564  @retval EFI_SECURITY_VIOLATION Recovery image is not processed due to security violation.
565**/
566EFI_STATUS
567ProcessRecoveryImage (
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_SUCESS            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**/
621EFI_STATUS
622ProcessFmpCapsuleImage (
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      //
650      Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);
651    }
652
653    Status = ProcessRecoveryImage (Image, ImageHeader->UpdateImageSize);
654    if (EFI_ERROR(Status)) {
655      return Status;
656    }
657  }
658
659  return EFI_SUCCESS;
660}
661
662/**
663  Process recovery capsule image.
664
665  Caution: This function may receive untrusted input.
666
667  @param[in] CapsuleBuffer   The capsule image buffer.
668  @param[in] CapsuleSize     The size of the capsule image in bytes.
669
670  @retval EFI_SUCCESS               The recovery capsule is processed.
671  @retval EFI_SECURITY_VIOLATION    The recovery capsule is not process because of security violation.
672  @retval EFI_NOT_FOUND             The recovery capsule is not process because of unrecognization.
673**/
674EFI_STATUS
675EFIAPI
676ProcessRecoveryCapsule (
677  IN VOID                                *CapsuleBuffer,
678  IN UINTN                               CapsuleSize
679  )
680{
681  EFI_STATUS                   Status;
682  BOOLEAN                      IsSystemFmp;
683  EFI_CAPSULE_HEADER           *CapsuleHeader;
684
685  CapsuleHeader = CapsuleBuffer;
686  if (!IsValidCapsuleHeader (CapsuleHeader, CapsuleSize)) {
687    DEBUG((DEBUG_ERROR, "CapsuleImageSize incorrect\n"));
688    return EFI_SECURITY_VIOLATION;
689  }
690
691  //
692  // Check FMP capsule layout
693  //
694  if (IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) {
695    DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule\n"));
696
697    DEBUG((DEBUG_INFO, "ProcessCapsuleImage for FmpCapsule ...\n"));
698    DEBUG((DEBUG_INFO, "ValidateFmpCapsule ...\n"));
699    Status = ValidateFmpCapsule(CapsuleHeader, &IsSystemFmp, NULL);
700    DEBUG((DEBUG_INFO, "ValidateFmpCapsule - %r\n", Status));
701    if (EFI_ERROR(Status)) {
702      return Status;
703    }
704
705    //
706    // Press EFI FMP Capsule
707    //
708    DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n"));
709    Status = ProcessFmpCapsuleImage(CapsuleHeader, IsSystemFmp);
710    DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status));
711
712    DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule Done\n"));
713    return Status;
714  }
715
716  return EFI_UNSUPPORTED;
717}
718
719/**
720  Loads a DXE capsule from some media into memory and updates the HOB table
721  with the DXE firmware volume information.
722
723  @param[in]  PeiServices   General-purpose services that are available to every PEIM.
724  @param[in]  This          Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.
725
726  @retval EFI_SUCCESS        The capsule was loaded correctly.
727  @retval EFI_DEVICE_ERROR   A device error occurred.
728  @retval EFI_NOT_FOUND      A recovery DXE capsule cannot be found.
729
730**/
731EFI_STATUS
732EFIAPI
733LoadRecoveryCapsule (
734  IN EFI_PEI_SERVICES                     **PeiServices,
735  IN EFI_PEI_RECOVERY_MODULE_PPI          *This
736  )
737{
738  EFI_STATUS                          Status;
739  EFI_PEI_DEVICE_RECOVERY_MODULE_PPI  *DeviceRecoveryPpi;
740  UINTN                               NumberRecoveryCapsules;
741  UINTN                               Instance;
742  UINTN                               CapsuleInstance;
743  UINTN                               CapsuleSize;
744  EFI_GUID                            CapsuleType;
745  VOID                                *CapsuleBuffer;
746
747  DEBUG((DEBUG_INFO | DEBUG_LOAD, "Recovery Entry\n"));
748
749  for (Instance = 0; ; Instance++) {
750    Status = PeiServicesLocatePpi (
751               &gEfiPeiDeviceRecoveryModulePpiGuid,
752               Instance,
753               NULL,
754               (VOID **)&DeviceRecoveryPpi
755               );
756    DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LocateRecoveryPpi (%d) - %r\n", Instance, Status));
757    if (EFI_ERROR (Status)) {
758      break;
759    }
760    NumberRecoveryCapsules = 0;
761    Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules (
762                                  (EFI_PEI_SERVICES **)PeiServices,
763                                  DeviceRecoveryPpi,
764                                  &NumberRecoveryCapsules
765                                  );
766    DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status));
767    if (EFI_ERROR (Status)) {
768      continue;
769    }
770    for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) {
771      CapsuleSize = 0;
772      Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo (
773                                    (EFI_PEI_SERVICES **)PeiServices,
774                                    DeviceRecoveryPpi,
775                                    FeaturePcdGet(PcdFrameworkCompatibilitySupport) ? CapsuleInstance - 1 : CapsuleInstance,
776                                    &CapsuleSize,
777                                    &CapsuleType
778                                    );
779      DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status));
780      if (EFI_ERROR (Status)) {
781        break;
782      }
783
784      CapsuleBuffer = AllocatePages (EFI_SIZE_TO_PAGES(CapsuleSize));
785      if (CapsuleBuffer == NULL) {
786        DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - AllocatePool fail\n"));
787        continue;
788      }
789      Status = DeviceRecoveryPpi->LoadRecoveryCapsule (
790                                    (EFI_PEI_SERVICES **)PeiServices,
791                                    DeviceRecoveryPpi,
792                                    FeaturePcdGet(PcdFrameworkCompatibilitySupport) ? CapsuleInstance - 1 : CapsuleInstance,
793                                    CapsuleBuffer
794                                    );
795      DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status));
796      if (EFI_ERROR (Status)) {
797        FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize));
798        break;
799      }
800      //
801      // good, load capsule buffer
802      //
803      Status = ProcessRecoveryCapsule (CapsuleBuffer, CapsuleSize);
804      return Status;
805    }
806  }
807
808  return EFI_NOT_FOUND;
809}
810