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