4 Caution: This module requires additional review when modified.
5 This module will have external input - Capsule-on-Disk Temp Relocation image.
6 This external input must be validated carefully to avoid security issue like
7 buffer overflow, integer overflow.
9 RetrieveRelocatedCapsule() will receive untrusted input and do basic validation.
11 Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
12 SPDX-License-Identifier: BSD-2-Clause-Patent
17 // The package level header files this module uses
23 // The protocols, PPI and GUID defintions for this module
25 #include <Ppi/MasterBootMode.h>
26 #include <Ppi/FirmwareVolumeInfo.h>
27 #include <Ppi/ReadOnlyVariable2.h>
28 #include <Ppi/Capsule.h>
29 #include <Ppi/CapsuleOnDisk.h>
30 #include <Ppi/DeviceRecoveryModule.h>
32 #include <Guid/FirmwareFileSystem2.h>
34 // The Library classes this module consumes
36 #include <Library/DebugLib.h>
37 #include <Library/PeimEntryPoint.h>
38 #include <Library/PeiServicesLib.h>
39 #include <Library/HobLib.h>
40 #include <Library/BaseMemoryLib.h>
41 #include <Library/MemoryAllocationLib.h>
42 #include <Library/PcdLib.h>
43 #include <Library/CapsuleLib.h>
44 #include <Library/ReportStatusCodeLib.h>
47 Loads a DXE capsule from some media into memory and updates the HOB table
48 with the DXE firmware volume information.
50 @param[in] PeiServices General-purpose services that are available to every PEIM.
51 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.
53 @retval EFI_SUCCESS The capsule was loaded correctly.
54 @retval EFI_DEVICE_ERROR A device error occurred.
55 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
61 IN EFI_PEI_SERVICES
**PeiServices
,
62 IN EDKII_PEI_CAPSULE_ON_DISK_PPI
*This
65 static EDKII_PEI_CAPSULE_ON_DISK_PPI mCapsuleOnDiskPpi
= {
69 static EFI_PEI_PPI_DESCRIPTOR mCapsuleOnDiskPpiList
= {
70 (EFI_PEI_PPI_DESCRIPTOR_PPI
| EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST
),
71 &gEdkiiPeiCapsuleOnDiskPpiGuid
,
76 Determine if capsule comes from memory by checking Capsule PPI.
78 @param[in] PeiServices General purpose services available to every PEIM.
80 @retval TRUE Capsule comes from memory.
81 @retval FALSE No capsule comes from memory.
87 IN CONST EFI_PEI_SERVICES
**PeiServices
91 PEI_CAPSULE_PPI
*Capsule
;
93 Status
= PeiServicesLocatePpi (
94 &gEfiPeiCapsulePpiGuid
,
99 if (!EFI_ERROR (Status
)) {
100 Status
= Capsule
->CheckCapsuleUpdate ((EFI_PEI_SERVICES
**)PeiServices
);
101 if (!EFI_ERROR (Status
)) {
110 Determine if it is a Capsule On Disk mode.
112 @retval TRUE Capsule On Disk mode.
113 @retval FALSE Not capsule On Disk mode.
118 IsCapsuleOnDiskMode (
124 EFI_PEI_READ_ONLY_VARIABLE2_PPI
*PPIVariableServices
;
125 BOOLEAN CodRelocInfo
;
127 Status
= PeiServicesLocatePpi (
128 &gEfiPeiReadOnlyVariable2PpiGuid
,
131 (VOID
**)&PPIVariableServices
133 ASSERT_EFI_ERROR (Status
);
135 Size
= sizeof (BOOLEAN
);
136 Status
= PPIVariableServices
->GetVariable (
138 COD_RELOCATION_INFO_VAR_NAME
,
139 &gEfiCapsuleVendorGuid
,
145 if (EFI_ERROR (Status
) || (Size
!= sizeof (BOOLEAN
)) || !CodRelocInfo
) {
146 DEBUG ((DEBUG_ERROR
, "Error Get CodRelocationInfo variable %r!\n", Status
));
154 Gets capsule images from relocated capsule buffer.
155 Create Capsule hob for each Capsule.
157 Caution: This function may receive untrusted input.
158 Capsule-on-Disk Temp Relocation image is external input, so this function
159 will validate Capsule-on-Disk Temp Relocation image to make sure the content
160 is read within the buffer.
162 @param[in] RelocCapsuleBuf Buffer pointer to the relocated capsule.
163 @param[in] RelocCapsuleTotalSize Total size of the relocated capsule.
165 @retval EFI_SUCCESS Succeed to get capsules and create hob.
166 @retval Others Fail to get capsules and create hob.
171 RetrieveRelocatedCapsule (
172 IN UINT8
*RelocCapsuleBuf
,
173 IN UINTN RelocCapsuleTotalSize
177 UINT8
*CapsuleDataBufEnd
;
180 UINT64 TotalImageSize
;
184 // Temp file contains at least 2 capsule (including 1 capsule name capsule) & 1 UINT64
186 if (RelocCapsuleTotalSize
< sizeof (UINT64
) + sizeof (EFI_CAPSULE_HEADER
) * 2) {
187 return EFI_INVALID_PARAMETER
;
190 CopyMem (&TotalImageSize
, RelocCapsuleBuf
, sizeof (UINT64
));
194 "ProcessRelocatedCapsule CapsuleBuf %x TotalCapSize %lx\n",
199 RelocCapsuleBuf
+= sizeof (UINT64
);
202 // TempCaspule file length check
204 if ((MAX_ADDRESS
- TotalImageSize
<= sizeof (UINT64
)) ||
205 ((UINT64
)RelocCapsuleTotalSize
!= TotalImageSize
+ sizeof (UINT64
)) ||
206 ((UINTN
)(MAX_ADDRESS
- (PHYSICAL_ADDRESS
)(UINTN
)RelocCapsuleBuf
) <= TotalImageSize
))
208 return EFI_INVALID_PARAMETER
;
211 CapsuleDataBufEnd
= RelocCapsuleBuf
+ TotalImageSize
;
214 // TempCapsule file integrity check over Capsule Header to ensure no data corruption in NV Var & Relocation storage
216 CapsulePtr
= RelocCapsuleBuf
;
219 while (CapsulePtr
< CapsuleDataBufEnd
) {
220 if (((CapsuleDataBufEnd
- CapsulePtr
) < sizeof (EFI_CAPSULE_HEADER
)) ||
221 (((EFI_CAPSULE_HEADER
*)CapsulePtr
)->CapsuleImageSize
< sizeof (EFI_CAPSULE_HEADER
)) ||
222 ((UINTN
)(MAX_ADDRESS
- (PHYSICAL_ADDRESS
)(UINTN
)CapsulePtr
) < ((EFI_CAPSULE_HEADER
*)CapsulePtr
)->CapsuleImageSize
)
228 CapsulePtr
+= ((EFI_CAPSULE_HEADER
*)CapsulePtr
)->CapsuleImageSize
;
232 if (CapsulePtr
!= CapsuleDataBufEnd
) {
233 return EFI_INVALID_PARAMETER
;
237 // Capsule count must be less than PcdCapsuleMax, avoid building too many CvHobs to occupy all the free space in HobList.
239 if (CapsuleNum
> PcdGet16 (PcdCapsuleMax
)) {
240 return EFI_INVALID_PARAMETER
;
244 // Re-iterate the capsule buffer to create Capsule hob & Capsule Name Str Hob for each Capsule saved in relocated capsule file
246 CapsulePtr
= RelocCapsuleBuf
;
248 while (CapsulePtr
< CapsuleDataBufEnd
) {
249 CapsuleSize
= ((EFI_CAPSULE_HEADER
*)CapsulePtr
)->CapsuleImageSize
;
250 BuildCvHob ((EFI_PHYSICAL_ADDRESS
)(UINTN
)CapsulePtr
, CapsuleSize
);
252 DEBUG ((DEBUG_INFO
, "Capsule saved in address %x size %x\n", CapsulePtr
, CapsuleSize
));
254 CapsulePtr
+= CapsuleSize
;
262 Recovery module entrypoint
264 @param[in] FileHandle Handle of the file being invoked.
265 @param[in] PeiServices Describes the list of possible PEI Services.
267 @return EFI_SUCCESS Recovery module is initialized.
271 InitializeCapsuleOnDiskLoad (
272 IN EFI_PEI_FILE_HANDLE FileHandle
,
273 IN CONST EFI_PEI_SERVICES
**PeiServices
280 BootMode
= GetBootModeHob ();
281 ASSERT (BootMode
== BOOT_ON_FLASH_UPDATE
);
284 // If there are capsules provisioned in memory, quit.
285 // Only one capsule resource is accept, CapsuleOnRam's priority is higher than CapsuleOnDisk.
287 if (CheckCapsuleFromRam (PeiServices
)) {
288 DEBUG ((DEBUG_ERROR
, "Capsule On Memory Detected! Quit.\n"));
293 VOID
*CapsuleOnDiskModePpi
;
295 if (!IsCapsuleOnDiskMode ()) {
296 return EFI_NOT_FOUND
;
300 // Check Capsule On Disk Relocation flag. If exists, load capsule & create Capsule Hob
302 Status
= PeiServicesLocatePpi (
303 &gEdkiiPeiBootInCapsuleOnDiskModePpiGuid
,
306 (VOID
**)&CapsuleOnDiskModePpi
308 if (EFI_ERROR (Status
)) {
309 DEBUG ((DEBUG_ERROR
, "Locate CapsuleOnDiskModePpi error %x\n", Status
));
315 Status
= PeiServicesInstallPpi (&mCapsuleOnDiskPpiList
);
316 ASSERT_EFI_ERROR (Status
);
318 FileNameSize
= PcdGetSize (PcdCoDRelocationFileName
);
319 Status
= PcdSetPtrS (PcdRecoveryFileName
, &FileNameSize
, (VOID
*)PcdGetPtr (PcdCoDRelocationFileName
));
320 ASSERT_EFI_ERROR (Status
);
326 Loads a DXE capsule from some media into memory and updates the HOB table
327 with the DXE firmware volume information.
329 @param[in] PeiServices General-purpose services that are available to every PEIM.
330 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.
332 @retval EFI_SUCCESS The capsule was loaded correctly.
333 @retval EFI_DEVICE_ERROR A device error occurred.
334 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
340 IN EFI_PEI_SERVICES
**PeiServices
,
341 IN EDKII_PEI_CAPSULE_ON_DISK_PPI
*This
345 EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
*DeviceRecoveryPpi
;
346 UINTN NumberRecoveryCapsules
;
348 UINTN CapsuleInstance
;
350 EFI_GUID CapsuleType
;
353 DEBUG ((DEBUG_INFO
| DEBUG_LOAD
, "Load Capsule On Disk Entry\n"));
355 for (Instance
= 0; ; Instance
++) {
356 Status
= PeiServicesLocatePpi (
357 &gEfiPeiDeviceRecoveryModulePpiGuid
,
360 (VOID
**)&DeviceRecoveryPpi
362 DEBUG ((DEBUG_INFO
, "LoadCapsuleOnDisk - LocateRecoveryPpi (%d) - %r\n", Instance
, Status
));
363 if (EFI_ERROR (Status
)) {
366 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
367 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_RECOVERY_PPI_NOT_FOUND
)
374 NumberRecoveryCapsules
= 0;
375 Status
= DeviceRecoveryPpi
->GetNumberRecoveryCapsules (
376 (EFI_PEI_SERVICES
**)PeiServices
,
378 &NumberRecoveryCapsules
380 DEBUG ((DEBUG_INFO
, "LoadCapsuleOnDisk - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules
, Status
));
381 if (EFI_ERROR (Status
)) {
385 for (CapsuleInstance
= 1; CapsuleInstance
<= NumberRecoveryCapsules
; CapsuleInstance
++) {
387 Status
= DeviceRecoveryPpi
->GetRecoveryCapsuleInfo (
388 (EFI_PEI_SERVICES
**)PeiServices
,
394 DEBUG ((DEBUG_INFO
, "LoadCapsuleOnDisk - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance
, CapsuleSize
, Status
));
395 if (EFI_ERROR (Status
)) {
400 // Allocate the memory so that it gets preserved into DXE.
401 // Capsule is special because it may need to populate to system table
403 CapsuleBuffer
= AllocateRuntimePages (EFI_SIZE_TO_PAGES (CapsuleSize
));
405 if (CapsuleBuffer
== NULL
) {
406 DEBUG ((DEBUG_ERROR
, "LoadCapsuleOnDisk - AllocateRuntimePages fail\n"));
410 Status
= DeviceRecoveryPpi
->LoadRecoveryCapsule (
411 (EFI_PEI_SERVICES
**)PeiServices
,
416 DEBUG ((DEBUG_INFO
, "LoadCapsuleOnDisk - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance
, Status
));
417 if (EFI_ERROR (Status
)) {
418 FreePages (CapsuleBuffer
, EFI_SIZE_TO_PAGES (CapsuleSize
));
423 // Capsule Update Mode, Split relocated Capsule buffer into different capsule vehical hobs.
425 Status
= RetrieveRelocatedCapsule (CapsuleBuffer
, CapsuleSize
);
430 if (EFI_ERROR (Status
)) {
432 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
433 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_NO_RECOVERY_CAPSULE
)
441 // Any attack against GPT, Relocation Info Variable or temp relocation file will result in no Capsule HOB and return EFI_NOT_FOUND.
442 // After flow to DXE phase. since no capsule hob is detected. Platform will clear Info flag and force restart.
443 // No volunerability will be exposed
446 return EFI_NOT_FOUND
;