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
));
192 DEBUG ((DEBUG_INFO
, "ProcessRelocatedCapsule CapsuleBuf %x TotalCapSize %lx\n",
193 RelocCapsuleBuf
, TotalImageSize
));
195 RelocCapsuleBuf
+= sizeof(UINT64
);
198 // TempCaspule file length check
200 if (MAX_ADDRESS
- TotalImageSize
<= sizeof(UINT64
) ||
201 (UINT64
)RelocCapsuleTotalSize
!= TotalImageSize
+ sizeof(UINT64
) ||
202 (UINTN
)(MAX_ADDRESS
- (PHYSICAL_ADDRESS
)(UINTN
)RelocCapsuleBuf
) <= TotalImageSize
) {
203 return EFI_INVALID_PARAMETER
;
206 CapsuleDataBufEnd
= RelocCapsuleBuf
+ TotalImageSize
;
209 // TempCapsule file integrity check over Capsule Header to ensure no data corruption in NV Var & Relocation storage
211 CapsulePtr
= RelocCapsuleBuf
;
214 while (CapsulePtr
< CapsuleDataBufEnd
) {
215 if ((CapsuleDataBufEnd
- CapsulePtr
) < sizeof(EFI_CAPSULE_HEADER
) ||
216 ((EFI_CAPSULE_HEADER
*)CapsulePtr
)->CapsuleImageSize
< sizeof(EFI_CAPSULE_HEADER
) ||
217 (UINTN
)(MAX_ADDRESS
- (PHYSICAL_ADDRESS
)(UINTN
)CapsulePtr
) < ((EFI_CAPSULE_HEADER
*)CapsulePtr
)->CapsuleImageSize
221 CapsulePtr
+= ((EFI_CAPSULE_HEADER
*)CapsulePtr
)->CapsuleImageSize
;
225 if (CapsulePtr
!= CapsuleDataBufEnd
) {
226 return EFI_INVALID_PARAMETER
;
230 // Capsule count must be less than PcdCapsuleMax, avoid building too many CvHobs to occupy all the free space in HobList.
232 if (CapsuleNum
> PcdGet16 (PcdCapsuleMax
)) {
233 return EFI_INVALID_PARAMETER
;
237 // Re-iterate the capsule buffer to create Capsule hob & Capsule Name Str Hob for each Capsule saved in relocated capsule file
239 CapsulePtr
= RelocCapsuleBuf
;
241 while (CapsulePtr
< CapsuleDataBufEnd
) {
242 CapsuleSize
= ((EFI_CAPSULE_HEADER
*)CapsulePtr
)->CapsuleImageSize
;
243 BuildCvHob ((EFI_PHYSICAL_ADDRESS
)(UINTN
)CapsulePtr
, CapsuleSize
);
245 DEBUG((DEBUG_INFO
, "Capsule saved in address %x size %x\n", CapsulePtr
, CapsuleSize
));
247 CapsulePtr
+= CapsuleSize
;
255 Recovery module entrypoint
257 @param[in] FileHandle Handle of the file being invoked.
258 @param[in] PeiServices Describes the list of possible PEI Services.
260 @return EFI_SUCCESS Recovery module is initialized.
264 InitializeCapsuleOnDiskLoad (
265 IN EFI_PEI_FILE_HANDLE FileHandle
,
266 IN CONST EFI_PEI_SERVICES
**PeiServices
273 BootMode
= GetBootModeHob();
274 ASSERT(BootMode
== BOOT_ON_FLASH_UPDATE
);
277 // If there are capsules provisioned in memory, quit.
278 // Only one capsule resource is accept, CapsuleOnRam's priority is higher than CapsuleOnDisk.
280 if (CheckCapsuleFromRam(PeiServices
)) {
281 DEBUG((DEBUG_ERROR
, "Capsule On Memory Detected! Quit.\n"));
286 VOID
*CapsuleOnDiskModePpi
;
288 if (!IsCapsuleOnDiskMode()){
289 return EFI_NOT_FOUND
;
293 // Check Capsule On Disk Relocation flag. If exists, load capsule & create Capsule Hob
295 Status
= PeiServicesLocatePpi (
296 &gEdkiiPeiBootInCapsuleOnDiskModePpiGuid
,
299 (VOID
**)&CapsuleOnDiskModePpi
301 if (EFI_ERROR(Status
)) {
302 DEBUG((DEBUG_ERROR
, "Locate CapsuleOnDiskModePpi error %x\n", Status
));
307 Status
= PeiServicesInstallPpi (&mCapsuleOnDiskPpiList
);
308 ASSERT_EFI_ERROR (Status
);
310 FileNameSize
= PcdGetSize (PcdCoDRelocationFileName
);
311 Status
= PcdSetPtrS (PcdRecoveryFileName
, &FileNameSize
, (VOID
*) PcdGetPtr(PcdCoDRelocationFileName
));
312 ASSERT_EFI_ERROR (Status
);
318 Loads a DXE capsule from some media into memory and updates the HOB table
319 with the DXE firmware volume information.
321 @param[in] PeiServices General-purpose services that are available to every PEIM.
322 @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.
324 @retval EFI_SUCCESS The capsule was loaded correctly.
325 @retval EFI_DEVICE_ERROR A device error occurred.
326 @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.
332 IN EFI_PEI_SERVICES
**PeiServices
,
333 IN EDKII_PEI_CAPSULE_ON_DISK_PPI
*This
337 EFI_PEI_DEVICE_RECOVERY_MODULE_PPI
*DeviceRecoveryPpi
;
338 UINTN NumberRecoveryCapsules
;
340 UINTN CapsuleInstance
;
342 EFI_GUID CapsuleType
;
345 DEBUG ((DEBUG_INFO
| DEBUG_LOAD
, "Load Capsule On Disk Entry\n"));
347 for (Instance
= 0; ; Instance
++) {
348 Status
= PeiServicesLocatePpi (
349 &gEfiPeiDeviceRecoveryModulePpiGuid
,
352 (VOID
**)&DeviceRecoveryPpi
354 DEBUG ((DEBUG_INFO
, "LoadCapsuleOnDisk - LocateRecoveryPpi (%d) - %r\n", Instance
, Status
));
355 if (EFI_ERROR (Status
)) {
358 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
359 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_RECOVERY_PPI_NOT_FOUND
)
364 NumberRecoveryCapsules
= 0;
365 Status
= DeviceRecoveryPpi
->GetNumberRecoveryCapsules (
366 (EFI_PEI_SERVICES
**)PeiServices
,
368 &NumberRecoveryCapsules
370 DEBUG ((DEBUG_INFO
, "LoadCapsuleOnDisk - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules
, Status
));
371 if (EFI_ERROR (Status
)) {
375 for (CapsuleInstance
= 1; CapsuleInstance
<= NumberRecoveryCapsules
; CapsuleInstance
++) {
377 Status
= DeviceRecoveryPpi
->GetRecoveryCapsuleInfo (
378 (EFI_PEI_SERVICES
**)PeiServices
,
384 DEBUG ((DEBUG_INFO
, "LoadCapsuleOnDisk - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance
, CapsuleSize
, Status
));
385 if (EFI_ERROR (Status
)) {
390 // Allocate the memory so that it gets preserved into DXE.
391 // Capsule is special because it may need to populate to system table
393 CapsuleBuffer
= AllocateRuntimePages (EFI_SIZE_TO_PAGES (CapsuleSize
));
395 if (CapsuleBuffer
== NULL
) {
396 DEBUG ((DEBUG_ERROR
, "LoadCapsuleOnDisk - AllocateRuntimePages fail\n"));
400 Status
= DeviceRecoveryPpi
->LoadRecoveryCapsule (
401 (EFI_PEI_SERVICES
**)PeiServices
,
406 DEBUG ((DEBUG_INFO
, "LoadCapsuleOnDisk - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance
, Status
));
407 if (EFI_ERROR (Status
)) {
408 FreePages (CapsuleBuffer
, EFI_SIZE_TO_PAGES(CapsuleSize
));
413 // Capsule Update Mode, Split relocated Capsule buffer into different capsule vehical hobs.
415 Status
= RetrieveRelocatedCapsule(CapsuleBuffer
, CapsuleSize
);
420 if (EFI_ERROR (Status
)) {
422 EFI_ERROR_CODE
| EFI_ERROR_MAJOR
,
423 (EFI_SOFTWARE_PEI_MODULE
| EFI_SW_PEI_EC_NO_RECOVERY_CAPSULE
)
431 // Any attack against GPT, Relocation Info Variable or temp relocation file will result in no Capsule HOB and return EFI_NOT_FOUND.
432 // After flow to DXE phase. since no capsule hob is detected. Platform will clear Info flag and force restart.
433 // No volunerability will be exposed
436 return EFI_NOT_FOUND
;