]>
Commit | Line | Data |
---|---|---|
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 | 12 | Copyright (c) 2016 - 2019, Intel Corporation. All rights reserved.<BR>\r |
fbf06957 | 13 | SPDX-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 | |
23 | // The protocols, PPI and GUID defintions for this module\r | |
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 | |
59 | EFI_STATUS\r | |
60 | EFIAPI\r | |
61 | LoadRecoveryCapsule (\r | |
62 | IN EFI_PEI_SERVICES **PeiServices,\r | |
63 | IN EFI_PEI_RECOVERY_MODULE_PPI *This\r | |
64 | );\r | |
65 | \r | |
66 | EFI_PEI_RECOVERY_MODULE_PPI mRecoveryPpi = {\r | |
67 | LoadRecoveryCapsule\r | |
68 | };\r | |
69 | \r | |
70 | EFI_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 | |
89 | EFI_STATUS\r | |
90 | ParseRecoveryDataFile (\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 | |
105 | BOOLEAN\r | |
106 | IsSystemFmpImage (\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 | |
134 | BOOLEAN\r | |
135 | IsFmpCapsuleGuid (\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 | |
147 | This function assumes the input Capusule image already passes basic check in\r | |
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 | |
160 | BOOLEAN\r | |
161 | IsSystemFmpCapsuleImage (\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 | |
207 | BOOLEAN\r | |
208 | IsValidCapsuleHeader (\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 | |
238 | @retval EFI_SUCESS Input capsule is a correct FMP capsule.\r | |
239 | @retval EFI_INVALID_PARAMETER Input capsule is not a correct FMP capsule.\r | |
240 | **/\r | |
241 | EFI_STATUS\r | |
242 | ValidateFmpCapsule (\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 | |
331 | if (ImageHeader->Version < EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {\r | |
332 | FmpImageHeaderSize = OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);\r | |
333 | }\r | |
334 | \r | |
335 | // No overflow\r | |
336 | if (FmpImageSize != (UINT64)FmpImageHeaderSize + (UINT64)ImageHeader->UpdateImageSize + (UINT64)ImageHeader->UpdateVendorCodeSize) {\r | |
337 | DEBUG((DEBUG_ERROR, "FmpImageSize(0x%lx) mismatch, UpdateImageSize(0x%x) UpdateVendorCodeSize(0x%x)\n", FmpImageSize, ImageHeader->UpdateImageSize, ImageHeader->UpdateVendorCodeSize));\r | |
338 | return EFI_INVALID_PARAMETER;\r | |
339 | }\r | |
340 | }\r | |
341 | \r | |
342 | if (ItemNum == 0) {\r | |
343 | //\r | |
344 | // No driver & payload element in FMP\r | |
345 | //\r | |
346 | EndOfPayload = (UINT8 *)(FmpCapsuleHeader + 1);\r | |
347 | if (EndOfPayload != EndOfCapsule) {\r | |
348 | DEBUG((DEBUG_ERROR, "EndOfPayload(0x%x) mismatch, EndOfCapsule(0x%x)\n", EndOfPayload, EndOfCapsule));\r | |
349 | return EFI_INVALID_PARAMETER;\r | |
350 | }\r | |
351 | return EFI_UNSUPPORTED;\r | |
352 | }\r | |
353 | \r | |
354 | //\r | |
355 | // Check in system FMP capsule\r | |
356 | //\r | |
357 | if (IsSystemFmp != NULL) {\r | |
358 | *IsSystemFmp = IsSystemFmpCapsuleImage(CapsuleHeader);\r | |
359 | }\r | |
360 | \r | |
361 | if (EmbeddedDriverCount != NULL) {\r | |
362 | *EmbeddedDriverCount = FmpCapsuleHeader->EmbeddedDriverCount;\r | |
363 | }\r | |
364 | \r | |
365 | return EFI_SUCCESS;\r | |
366 | }\r | |
367 | \r | |
368 | /**\r | |
369 | Recovery module entrypoint\r | |
370 | \r | |
371 | @param[in] FileHandle Handle of the file being invoked.\r | |
372 | @param[in] PeiServices Describes the list of possible PEI Services.\r | |
373 | \r | |
374 | @return EFI_SUCCESS Recovery module is initialized.\r | |
375 | **/\r | |
376 | EFI_STATUS\r | |
377 | EFIAPI\r | |
378 | InitializeRecoveryModule (\r | |
379 | IN EFI_PEI_FILE_HANDLE FileHandle,\r | |
380 | IN CONST EFI_PEI_SERVICES **PeiServices\r | |
381 | )\r | |
382 | {\r | |
383 | EFI_STATUS Status;\r | |
384 | UINTN BootMode;\r | |
385 | \r | |
386 | BootMode = GetBootModeHob();\r | |
387 | ASSERT(BootMode == BOOT_IN_RECOVERY_MODE);\r | |
388 | \r | |
389 | Status = (**PeiServices).InstallPpi (PeiServices, &mRecoveryPpiList);\r | |
390 | ASSERT_EFI_ERROR (Status);\r | |
391 | \r | |
392 | return Status;\r | |
393 | }\r | |
394 | \r | |
395 | /**\r | |
396 | Create hob and install FvInfo PPI for recovery capsule.\r | |
397 | \r | |
398 | @param[in] FvImage Points to the DXE FV image.\r | |
399 | @param[in] FvImageSize The length of the DXE FV image in bytes.\r | |
400 | \r | |
401 | @retval EFI_SUCESS Create hob and install FvInfo PPI successfully.\r | |
402 | @retval EFI_VOLUME_CORRUPTED The input data is not an FV.\r | |
403 | @retval EFI_OUT_OF_RESOURCES No enough resource to process the input data.\r | |
404 | **/\r | |
405 | EFI_STATUS\r | |
406 | EFIAPI\r | |
407 | CreateHobForRecoveryCapsule (\r | |
408 | IN VOID *FvImage,\r | |
409 | IN UINTN FvImageSize\r | |
410 | )\r | |
411 | {\r | |
412 | EFI_FIRMWARE_VOLUME_HEADER *FvHeader;\r | |
413 | UINT32 FvAlignment;\r | |
414 | UINT64 FvLength;\r | |
415 | VOID *NewFvBuffer;\r | |
416 | \r | |
417 | //\r | |
418 | // FvImage should be at its required alignment.\r | |
419 | //\r | |
420 | FvHeader = (EFI_FIRMWARE_VOLUME_HEADER *) FvImage;\r | |
421 | //\r | |
422 | // Validate FV Header, if not as expected, return\r | |
423 | //\r | |
424 | if (ReadUnaligned32 (&FvHeader->Signature) != EFI_FVH_SIGNATURE) {\r | |
425 | DEBUG((DEBUG_ERROR, "CreateHobForRecoveryCapsule (Fv Signature Error)\n"));\r | |
426 | return EFI_VOLUME_CORRUPTED;\r | |
427 | }\r | |
428 | //\r | |
429 | // If EFI_FVB2_WEAK_ALIGNMENT is set in the volume header then the first byte of the volume\r | |
430 | // can be aligned on any power-of-two boundary. A weakly aligned volume can not be moved from\r | |
431 | // its initial linked location and maintain its alignment.\r | |
432 | //\r | |
433 | if ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_WEAK_ALIGNMENT) != EFI_FVB2_WEAK_ALIGNMENT) {\r | |
434 | //\r | |
435 | // Get FvHeader alignment\r | |
436 | //\r | |
437 | FvAlignment = 1 << ((ReadUnaligned32 (&FvHeader->Attributes) & EFI_FVB2_ALIGNMENT) >> 16);\r | |
438 | //\r | |
439 | // FvAlignment must be greater than or equal to 8 bytes of the minimum FFS alignment value.\r | |
440 | //\r | |
441 | if (FvAlignment < 8) {\r | |
442 | FvAlignment = 8;\r | |
443 | }\r | |
444 | //\r | |
445 | // Allocate the aligned buffer for the FvImage.\r | |
446 | //\r | |
447 | if ((UINTN) FvHeader % FvAlignment != 0) {\r | |
448 | DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule (FvHeader 0x%lx is not aligned)\n", (UINT64)(UINTN)FvHeader));\r | |
449 | FvLength = ReadUnaligned64 (&FvHeader->FvLength);\r | |
450 | NewFvBuffer = AllocateAlignedPages (EFI_SIZE_TO_PAGES ((UINTN) FvLength), FvAlignment);\r | |
451 | if (NewFvBuffer == NULL) {\r | |
452 | DEBUG((DEBUG_ERROR, "CreateHobForRecoveryCapsule (Not enough resource to allocate 0x%lx bytes)\n", FvLength));\r | |
453 | return EFI_OUT_OF_RESOURCES;\r | |
454 | }\r | |
455 | CopyMem (NewFvBuffer, FvHeader, (UINTN) FvLength);\r | |
456 | FvHeader = (EFI_FIRMWARE_VOLUME_HEADER*) NewFvBuffer;\r | |
457 | }\r | |
458 | }\r | |
459 | \r | |
460 | BuildFvHob((UINT64)(UINTN)FvHeader, FvHeader->FvLength);\r | |
461 | DEBUG((DEBUG_INFO, "BuildFvHob (FV in recovery) - 0x%lx - 0x%lx\n", (UINT64)(UINTN)FvHeader, FvHeader->FvLength));\r | |
462 | \r | |
463 | PeiServicesInstallFvInfoPpi(\r | |
2ed235fc | 464 | &FvHeader->FileSystemGuid,\r |
e470ee6d JY |
465 | (VOID *)FvHeader,\r |
466 | (UINT32)FvHeader->FvLength,\r | |
467 | NULL,\r | |
468 | NULL\r | |
469 | );\r | |
470 | \r | |
471 | return EFI_SUCCESS;\r | |
472 | }\r | |
473 | \r | |
474 | /**\r | |
475 | Create recovery context based upon System Firmware image and config file.\r | |
476 | \r | |
477 | @param[in] SystemFirmwareImage Points to the System Firmware image.\r | |
478 | @param[in] SystemFirmwareImageSize The length of the System Firmware image in bytes.\r | |
479 | @param[in] ConfigImage Points to the config file image.\r | |
480 | @param[in] ConfigImageSize The length of the config file image in bytes.\r | |
481 | \r | |
482 | @retval EFI_SUCESS Process Recovery Image successfully.\r | |
483 | **/\r | |
484 | EFI_STATUS\r | |
485 | RecoverImage (\r | |
486 | IN VOID *SystemFirmwareImage,\r | |
487 | IN UINTN SystemFirmwareImageSize,\r | |
488 | IN VOID *ConfigImage,\r | |
489 | IN UINTN ConfigImageSize\r | |
490 | )\r | |
491 | {\r | |
492 | EFI_STATUS Status;\r | |
493 | RECOVERY_CONFIG_DATA *ConfigData;\r | |
494 | RECOVERY_CONFIG_DATA *RecoveryConfigData;\r | |
495 | CONFIG_HEADER ConfigHeader;\r | |
496 | UINTN Index;\r | |
497 | \r | |
498 | if (ConfigImage == NULL) {\r | |
499 | DEBUG((DEBUG_INFO, "RecoverImage (NoConfig)\n"));\r | |
500 | Status = CreateHobForRecoveryCapsule(\r | |
501 | SystemFirmwareImage,\r | |
502 | SystemFirmwareImageSize\r | |
503 | );\r | |
504 | return Status;\r | |
505 | }\r | |
506 | \r | |
507 | ConfigData = NULL;\r | |
508 | ZeroMem (&ConfigHeader, sizeof(ConfigHeader));\r | |
509 | Status = ParseRecoveryDataFile (\r | |
510 | ConfigImage,\r | |
511 | ConfigImageSize,\r | |
512 | &ConfigHeader,\r | |
513 | &ConfigData\r | |
514 | );\r | |
515 | DEBUG((DEBUG_INFO, "ParseRecoveryDataFile - %r\n", Status));\r | |
516 | if (EFI_ERROR(Status)) {\r | |
517 | return Status;\r | |
518 | }\r | |
519 | DEBUG((DEBUG_INFO, "ConfigHeader.NumOfRecovery - 0x%x\n", ConfigHeader.NumOfRecovery));\r | |
520 | DEBUG((DEBUG_INFO, "PcdEdkiiSystemFirmwareFileGuid - %g\n", PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid)));\r | |
521 | \r | |
522 | Index = 0;\r | |
523 | RecoveryConfigData = ConfigData;\r | |
524 | while (Index < ConfigHeader.NumOfRecovery) {\r | |
525 | if (CompareGuid(&RecoveryConfigData->FileGuid, PcdGetPtr(PcdEdkiiSystemFirmwareFileGuid))) {\r | |
526 | DEBUG((DEBUG_INFO, "FileGuid - %g (processing)\n", &RecoveryConfigData->FileGuid));\r | |
527 | Status = CreateHobForRecoveryCapsule (\r | |
528 | (UINT8 *)SystemFirmwareImage + RecoveryConfigData->ImageOffset,\r | |
529 | RecoveryConfigData->Length\r | |
530 | );\r | |
531 | //\r | |
532 | // Shall updates be serialized so that if a recovery FV is not successfully completed,\r | |
533 | // the remaining updates won't be performed.\r | |
534 | //\r | |
535 | if (EFI_ERROR (Status)) {\r | |
536 | break;\r | |
537 | }\r | |
538 | } else {\r | |
539 | DEBUG((DEBUG_INFO, "FileGuid - %g (ignored)\n", &RecoveryConfigData->FileGuid));\r | |
540 | }\r | |
541 | \r | |
542 | Index++;\r | |
543 | RecoveryConfigData++;\r | |
544 | }\r | |
545 | \r | |
546 | return Status;\r | |
547 | }\r | |
548 | \r | |
549 | /**\r | |
550 | Process recovery image.\r | |
551 | \r | |
552 | Caution: This function may receive untrusted input.\r | |
553 | \r | |
554 | @param[in] Image Points to the recovery image.\r | |
555 | @param[in] Length The length of the recovery image in bytes.\r | |
556 | \r | |
557 | @retval EFI_SUCESS Process Recovery Image successfully.\r | |
558 | @retval EFI_SECURITY_VIOLATION Recovery image is not processed due to security violation.\r | |
559 | **/\r | |
560 | EFI_STATUS\r | |
561 | ProcessRecoveryImage (\r | |
562 | IN VOID *Image,\r | |
563 | IN UINTN Length\r | |
564 | )\r | |
565 | {\r | |
566 | UINT32 LastAttemptVersion;\r | |
567 | UINT32 LastAttemptStatus;\r | |
568 | EFI_STATUS Status;\r | |
569 | VOID *SystemFirmwareImage;\r | |
570 | UINTN SystemFirmwareImageSize;\r | |
571 | VOID *ConfigImage;\r | |
572 | UINTN ConfigImageSize;\r | |
573 | VOID *AuthenticatedImage;\r | |
574 | UINTN AuthenticatedImageSize;\r | |
575 | \r | |
8b66342c HW |
576 | AuthenticatedImage = NULL;\r |
577 | AuthenticatedImageSize = 0;\r | |
578 | \r | |
e470ee6d JY |
579 | Status = CapsuleAuthenticateSystemFirmware(Image, Length, TRUE, &LastAttemptVersion, &LastAttemptStatus, &AuthenticatedImage, &AuthenticatedImageSize);\r |
580 | if (EFI_ERROR(Status)) {\r | |
581 | DEBUG((DEBUG_INFO, "CapsuleAuthenticateSystemFirmware - %r\n", Status));\r | |
582 | return Status;\r | |
583 | }\r | |
584 | \r | |
585 | ExtractSystemFirmwareImage(AuthenticatedImage, AuthenticatedImageSize, &SystemFirmwareImage, &SystemFirmwareImageSize);\r | |
586 | ExtractConfigImage(AuthenticatedImage, AuthenticatedImageSize, &ConfigImage, &ConfigImageSize);\r | |
587 | \r | |
588 | Status = RecoverImage(SystemFirmwareImage, SystemFirmwareImageSize, ConfigImage, ConfigImageSize);\r | |
589 | if (EFI_ERROR(Status)) {\r | |
590 | DEBUG((DEBUG_INFO, "RecoverImage - %r\n", Status));\r | |
591 | return Status;\r | |
592 | }\r | |
593 | \r | |
594 | return EFI_SUCCESS;\r | |
595 | }\r | |
596 | \r | |
597 | /**\r | |
598 | Process Firmware management protocol data capsule.\r | |
599 | \r | |
600 | Caution: This function may receive untrusted input.\r | |
601 | \r | |
602 | This function assumes the caller validated the capsule by using\r | |
603 | ValidateFmpCapsule(), so that all fields in EFI_CAPSULE_HEADER,\r | |
604 | EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER and\r | |
605 | EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER are correct.\r | |
606 | \r | |
607 | @param[in] CapsuleHeader Points to a capsule header.\r | |
608 | @param[in] IsSystemFmp If this capsule is a system FMP capsule.\r | |
609 | \r | |
610 | @retval EFI_SUCESS Process Capsule Image successfully.\r | |
611 | @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware.\r | |
612 | @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted.\r | |
613 | @retval EFI_OUT_OF_RESOURCES Not enough memory.\r | |
614 | **/\r | |
615 | EFI_STATUS\r | |
616 | ProcessFmpCapsuleImage (\r | |
617 | IN EFI_CAPSULE_HEADER *CapsuleHeader,\r | |
618 | IN BOOLEAN IsSystemFmp\r | |
619 | )\r | |
620 | {\r | |
621 | EFI_STATUS Status;\r | |
622 | EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r | |
623 | EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r | |
624 | UINT8 *Image;\r | |
625 | UINT64 *ItemOffsetList;\r | |
626 | UINTN ItemIndex;\r | |
627 | \r | |
628 | if (!IsSystemFmp) {\r | |
629 | return EFI_UNSUPPORTED;\r | |
630 | }\r | |
631 | \r | |
632 | FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *)((UINT8 *)CapsuleHeader + CapsuleHeader->HeaderSize);\r | |
633 | ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r | |
634 | \r | |
635 | for (ItemIndex = 0; ItemIndex < FmpCapsuleHeader->PayloadItemCount; ItemIndex++) {\r | |
636 | ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemIndex]);\r | |
637 | if (ImageHeader->Version >= EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION) {\r | |
638 | Image = (UINT8 *)(ImageHeader + 1);\r | |
639 | } else {\r | |
640 | //\r | |
641 | // If the EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER is version 1, only match ImageTypeId.\r | |
642 | // Header should exclude UpdateHardwareInstance field\r | |
643 | //\r | |
644 | Image = (UINT8 *)ImageHeader + OFFSET_OF(EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER, UpdateHardwareInstance);\r | |
645 | }\r | |
646 | \r | |
647 | Status = ProcessRecoveryImage (Image, ImageHeader->UpdateImageSize);\r | |
648 | if (EFI_ERROR(Status)) {\r | |
649 | return Status;\r | |
650 | }\r | |
651 | }\r | |
652 | \r | |
653 | return EFI_SUCCESS;\r | |
654 | }\r | |
655 | \r | |
656 | /**\r | |
657 | Process recovery capsule image.\r | |
658 | \r | |
659 | Caution: This function may receive untrusted input.\r | |
660 | \r | |
661 | @param[in] CapsuleBuffer The capsule image buffer.\r | |
662 | @param[in] CapsuleSize The size of the capsule image in bytes.\r | |
663 | \r | |
664 | @retval EFI_SUCCESS The recovery capsule is processed.\r | |
665 | @retval EFI_SECURITY_VIOLATION The recovery capsule is not process because of security violation.\r | |
666 | @retval EFI_NOT_FOUND The recovery capsule is not process because of unrecognization.\r | |
667 | **/\r | |
668 | EFI_STATUS\r | |
669 | EFIAPI\r | |
670 | ProcessRecoveryCapsule (\r | |
671 | IN VOID *CapsuleBuffer,\r | |
672 | IN UINTN CapsuleSize\r | |
673 | )\r | |
674 | {\r | |
675 | EFI_STATUS Status;\r | |
676 | BOOLEAN IsSystemFmp;\r | |
677 | EFI_CAPSULE_HEADER *CapsuleHeader;\r | |
678 | \r | |
679 | CapsuleHeader = CapsuleBuffer;\r | |
680 | if (!IsValidCapsuleHeader (CapsuleHeader, CapsuleSize)) {\r | |
681 | DEBUG((DEBUG_ERROR, "CapsuleImageSize incorrect\n"));\r | |
682 | return EFI_SECURITY_VIOLATION;\r | |
683 | }\r | |
684 | \r | |
685 | //\r | |
686 | // Check FMP capsule layout\r | |
687 | //\r | |
688 | if (IsFmpCapsuleGuid(&CapsuleHeader->CapsuleGuid)) {\r | |
689 | DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule\n"));\r | |
690 | \r | |
691 | DEBUG((DEBUG_INFO, "ProcessCapsuleImage for FmpCapsule ...\n"));\r | |
692 | DEBUG((DEBUG_INFO, "ValidateFmpCapsule ...\n"));\r | |
693 | Status = ValidateFmpCapsule(CapsuleHeader, &IsSystemFmp, NULL);\r | |
694 | DEBUG((DEBUG_INFO, "ValidateFmpCapsule - %r\n", Status));\r | |
695 | if (EFI_ERROR(Status)) {\r | |
696 | return Status;\r | |
697 | }\r | |
698 | \r | |
699 | //\r | |
70bd2a85 | 700 | // Process EFI FMP Capsule\r |
e470ee6d JY |
701 | //\r |
702 | DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage ...\n"));\r | |
703 | Status = ProcessFmpCapsuleImage(CapsuleHeader, IsSystemFmp);\r | |
704 | DEBUG((DEBUG_INFO, "ProcessFmpCapsuleImage - %r\n", Status));\r | |
705 | \r | |
706 | DEBUG((DEBUG_INFO, "CreateHobForRecoveryCapsule Done\n"));\r | |
707 | return Status;\r | |
708 | }\r | |
709 | \r | |
710 | return EFI_UNSUPPORTED;\r | |
711 | }\r | |
712 | \r | |
713 | /**\r | |
714 | Loads a DXE capsule from some media into memory and updates the HOB table\r | |
715 | with the DXE firmware volume information.\r | |
716 | \r | |
717 | @param[in] PeiServices General-purpose services that are available to every PEIM.\r | |
718 | @param[in] This Indicates the EFI_PEI_RECOVERY_MODULE_PPI instance.\r | |
719 | \r | |
720 | @retval EFI_SUCCESS The capsule was loaded correctly.\r | |
721 | @retval EFI_DEVICE_ERROR A device error occurred.\r | |
722 | @retval EFI_NOT_FOUND A recovery DXE capsule cannot be found.\r | |
723 | \r | |
724 | **/\r | |
725 | EFI_STATUS\r | |
726 | EFIAPI\r | |
727 | LoadRecoveryCapsule (\r | |
728 | IN EFI_PEI_SERVICES **PeiServices,\r | |
729 | IN EFI_PEI_RECOVERY_MODULE_PPI *This\r | |
730 | )\r | |
731 | {\r | |
732 | EFI_STATUS Status;\r | |
733 | EFI_PEI_DEVICE_RECOVERY_MODULE_PPI *DeviceRecoveryPpi;\r | |
734 | UINTN NumberRecoveryCapsules;\r | |
735 | UINTN Instance;\r | |
736 | UINTN CapsuleInstance;\r | |
737 | UINTN CapsuleSize;\r | |
738 | EFI_GUID CapsuleType;\r | |
739 | VOID *CapsuleBuffer;\r | |
740 | \r | |
741 | DEBUG((DEBUG_INFO | DEBUG_LOAD, "Recovery Entry\n"));\r | |
742 | \r | |
743 | for (Instance = 0; ; Instance++) {\r | |
744 | Status = PeiServicesLocatePpi (\r | |
745 | &gEfiPeiDeviceRecoveryModulePpiGuid,\r | |
746 | Instance,\r | |
747 | NULL,\r | |
748 | (VOID **)&DeviceRecoveryPpi\r | |
749 | );\r | |
750 | DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LocateRecoveryPpi (%d) - %r\n", Instance, Status));\r | |
751 | if (EFI_ERROR (Status)) {\r | |
752 | break;\r | |
753 | }\r | |
754 | NumberRecoveryCapsules = 0;\r | |
755 | Status = DeviceRecoveryPpi->GetNumberRecoveryCapsules (\r | |
756 | (EFI_PEI_SERVICES **)PeiServices,\r | |
757 | DeviceRecoveryPpi,\r | |
758 | &NumberRecoveryCapsules\r | |
759 | );\r | |
760 | DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetNumberRecoveryCapsules (%d) - %r\n", NumberRecoveryCapsules, Status));\r | |
761 | if (EFI_ERROR (Status)) {\r | |
762 | continue;\r | |
763 | }\r | |
764 | for (CapsuleInstance = 1; CapsuleInstance <= NumberRecoveryCapsules; CapsuleInstance++) {\r | |
765 | CapsuleSize = 0;\r | |
766 | Status = DeviceRecoveryPpi->GetRecoveryCapsuleInfo (\r | |
767 | (EFI_PEI_SERVICES **)PeiServices,\r | |
768 | DeviceRecoveryPpi,\r | |
868f139b | 769 | CapsuleInstance,\r |
e470ee6d JY |
770 | &CapsuleSize,\r |
771 | &CapsuleType\r | |
772 | );\r | |
773 | DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - GetRecoveryCapsuleInfo (%d - %x) - %r\n", CapsuleInstance, CapsuleSize, Status));\r | |
774 | if (EFI_ERROR (Status)) {\r | |
775 | break;\r | |
776 | }\r | |
777 | \r | |
778 | CapsuleBuffer = AllocatePages (EFI_SIZE_TO_PAGES(CapsuleSize));\r | |
779 | if (CapsuleBuffer == NULL) {\r | |
780 | DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - AllocatePool fail\n"));\r | |
781 | continue;\r | |
782 | }\r | |
783 | Status = DeviceRecoveryPpi->LoadRecoveryCapsule (\r | |
784 | (EFI_PEI_SERVICES **)PeiServices,\r | |
785 | DeviceRecoveryPpi,\r | |
868f139b | 786 | CapsuleInstance,\r |
e470ee6d JY |
787 | CapsuleBuffer\r |
788 | );\r | |
789 | DEBUG ((DEBUG_ERROR, "LoadRecoveryCapsule - LoadRecoveryCapsule (%d) - %r\n", CapsuleInstance, Status));\r | |
790 | if (EFI_ERROR (Status)) {\r | |
791 | FreePages (CapsuleBuffer, EFI_SIZE_TO_PAGES(CapsuleSize));\r | |
792 | break;\r | |
793 | }\r | |
794 | //\r | |
795 | // good, load capsule buffer\r | |
796 | //\r | |
797 | Status = ProcessRecoveryCapsule (CapsuleBuffer, CapsuleSize);\r | |
798 | return Status;\r | |
799 | }\r | |
800 | }\r | |
801 | \r | |
802 | return EFI_NOT_FOUND;\r | |
803 | }\r |