]> git.proxmox.com Git - mirror_edk2.git/blame_incremental - IntelFrameworkModulePkg/Library/DxeCapsuleLib/DxeCapsuleLib.c
IntelFrameworkModulePkg/DxeCapsuleLib: Add ProcessCapsules().
[mirror_edk2.git] / IntelFrameworkModulePkg / Library / DxeCapsuleLib / DxeCapsuleLib.c
... / ...
CommitLineData
1/** @file\r
2 Capsule Library instance to process capsule images.\r
3\r
4 Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>\r
5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15#include <PiDxe.h>\r
16\r
17#include <Guid/Capsule.h>\r
18#include <Guid/FmpCapsule.h>\r
19\r
20#include <Library/DebugLib.h>\r
21#include <Library/BaseMemoryLib.h>\r
22#include <Library/DxeServicesTableLib.h>\r
23#include <Library/MemoryAllocationLib.h>\r
24#include <Library/CapsuleLib.h>\r
25#include <Library/GenericBdsLib.h>\r
26#include <Library/UefiBootServicesTableLib.h>\r
27#include <Library/BaseLib.h>\r
28#include <Library/DevicePathLib.h>\r
29\r
30#include <Protocol/FirmwareManagement.h>\r
31#include <Protocol/DevicePath.h>\r
32\r
33\r
34/**\r
35 Function indicate the current completion progress of the firmware\r
36 update. Platform may override with own specific progress function.\r
37\r
38 @param Completion A value between 1 and 100 indicating the current completion progress of the firmware update\r
39\r
40 @retval EFI_SUCESS Input capsule is a correct FMP capsule.\r
41**/\r
42EFI_STATUS\r
43EFIAPI\r
44Update_Image_Progress (\r
45 IN UINTN Completion\r
46)\r
47{\r
48 return EFI_SUCCESS;\r
49}\r
50\r
51\r
52/**\r
53 Validate Fmp capsules layout.\r
54\r
55 @param CapsuleHeader Points to a capsule header.\r
56\r
57 @retval EFI_SUCESS Input capsule is a correct FMP capsule.\r
58 @retval EFI_INVALID_PARAMETER Input capsule is not a correct FMP capsule.\r
59**/\r
60EFI_STATUS\r
61ValidateFmpCapsule (\r
62 IN EFI_CAPSULE_HEADER *CapsuleHeader\r
63 )\r
64{\r
65 EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
66 UINT8 *EndOfCapsule;\r
67 EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
68 UINT8 *EndOfPayload;\r
69 UINT64 *ItemOffsetList;\r
70 UINT32 ItemNum;\r
71 UINTN Index;\r
72\r
73 FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
74 EndOfCapsule = (UINT8 *) CapsuleHeader + CapsuleHeader->CapsuleImageSize;\r
75\r
76 if (FmpCapsuleHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {\r
77 return EFI_INVALID_PARAMETER;\r
78 }\r
79 ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
80\r
81 ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;\r
82\r
83 if (ItemNum == FmpCapsuleHeader->EmbeddedDriverCount) {\r
84 //\r
85 // No payload element \r
86 //\r
87 if (((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemNum - 1]) < EndOfCapsule) {\r
88 return EFI_SUCCESS;\r
89 } else {\r
90 return EFI_INVALID_PARAMETER;\r
91 }\r
92 }\r
93\r
94 if (FmpCapsuleHeader->PayloadItemCount != 0) {\r
95 //\r
96 // Check if the last payload is within capsule image range\r
97 //\r
98 ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[ItemNum - 1]);\r
99 EndOfPayload = (UINT8 *)(ImageHeader + 1) + ImageHeader->UpdateImageSize + ImageHeader->UpdateVendorCodeSize;\r
100 } else {\r
101 //\r
102 // No driver & payload element in FMP\r
103 //\r
104 EndOfPayload = (UINT8 *)(FmpCapsuleHeader + 1);\r
105 }\r
106\r
107 if (EndOfPayload != EndOfCapsule) {\r
108 return EFI_INVALID_PARAMETER;\r
109 }\r
110\r
111 //\r
112 // All the address in ItemOffsetList must be stored in ascending order\r
113 //\r
114 if (ItemNum >= 2) {\r
115 for (Index = 0; Index < ItemNum - 1; Index++) {\r
116 if (ItemOffsetList[Index] >= ItemOffsetList[Index + 1]) {\r
117 return EFI_INVALID_PARAMETER;\r
118 }\r
119 }\r
120 }\r
121\r
122 return EFI_SUCCESS;\r
123}\r
124\r
125/**\r
126 Process Firmware management protocol data capsule. \r
127\r
128 @param CapsuleHeader Points to a capsule header.\r
129\r
130 @retval EFI_SUCESS Process Capsule Image successfully.\r
131 @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware.\r
132 @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted.\r
133 @retval EFI_OUT_OF_RESOURCES Not enough memory.\r
134**/\r
135EFI_STATUS\r
136ProcessFmpCapsuleImage (\r
137 IN EFI_CAPSULE_HEADER *CapsuleHeader\r
138 )\r
139{\r
140 EFI_STATUS Status;\r
141 EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *FmpCapsuleHeader;\r
142 EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader;\r
143 EFI_HANDLE ImageHandle;\r
144 UINT64 *ItemOffsetList;\r
145 UINT32 ItemNum;\r
146 UINTN Index;\r
147 UINTN ExitDataSize;\r
148 EFI_HANDLE *HandleBuffer;\r
149 EFI_FIRMWARE_MANAGEMENT_PROTOCOL *Fmp;\r
150 UINTN NumberOfHandles;\r
151 UINTN DescriptorSize;\r
152 UINT8 FmpImageInfoCount;\r
153 UINT32 FmpImageInfoDescriptorVer;\r
154 UINTN ImageInfoSize;\r
155 UINT32 PackageVersion;\r
156 CHAR16 *PackageVersionName;\r
157 CHAR16 *AbortReason;\r
158 EFI_FIRMWARE_IMAGE_DESCRIPTOR *FmpImageInfoBuf;\r
159 EFI_FIRMWARE_IMAGE_DESCRIPTOR *TempFmpImageInfo;\r
160 UINTN DriverLen;\r
161 UINTN Index1;\r
162 UINTN Index2;\r
163 MEMMAP_DEVICE_PATH MemMapNode;\r
164 EFI_DEVICE_PATH_PROTOCOL *DriverDevicePath;\r
165\r
166 Status = EFI_SUCCESS;\r
167 HandleBuffer = NULL;\r
168 ExitDataSize = 0;\r
169 DriverDevicePath = NULL;\r
170\r
171 FmpCapsuleHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
172\r
173 if (FmpCapsuleHeader->Version > EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER_INIT_VERSION) {\r
174 return EFI_INVALID_PARAMETER;\r
175 }\r
176 ItemOffsetList = (UINT64 *)(FmpCapsuleHeader + 1);\r
177\r
178 ItemNum = FmpCapsuleHeader->EmbeddedDriverCount + FmpCapsuleHeader->PayloadItemCount;\r
179\r
180 //\r
181 // capsule in which driver count and payload count are both zero is not processed.\r
182 //\r
183 if (ItemNum == 0) {\r
184 return EFI_SUCCESS;\r
185 }\r
186\r
187 //\r
188 // 1. ConnectAll to ensure \r
189 // All the communication protocol required by driver in capsule installed \r
190 // All FMP protocols are installed\r
191 //\r
192 BdsLibConnectAll();\r
193\r
194\r
195 //\r
196 // 2. Try to load & start all the drivers within capsule \r
197 //\r
198 SetDevicePathNodeLength (&MemMapNode.Header, sizeof (MemMapNode));\r
199 MemMapNode.Header.Type = HARDWARE_DEVICE_PATH;\r
200 MemMapNode.Header.SubType = HW_MEMMAP_DP;\r
201 MemMapNode.MemoryType = EfiBootServicesCode;\r
202 MemMapNode.StartingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CapsuleHeader;\r
203 MemMapNode.EndingAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)((UINT8 *)CapsuleHeader + CapsuleHeader->CapsuleImageSize - 1);\r
204\r
205 DriverDevicePath = AppendDevicePathNode (NULL, &MemMapNode.Header);\r
206 if (DriverDevicePath == NULL) {\r
207 return EFI_OUT_OF_RESOURCES;\r
208 }\r
209\r
210 for (Index = 0; Index < FmpCapsuleHeader->EmbeddedDriverCount; Index++) {\r
211 if (FmpCapsuleHeader->PayloadItemCount == 0 && Index == (UINTN)FmpCapsuleHeader->EmbeddedDriverCount - 1) {\r
212 //\r
213 // When driver is last element in the ItemOffsetList array, the driver size is calculated by reference CapsuleImageSize in EFI_CAPSULE_HEADER\r
214 //\r
215 DriverLen = CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize - (UINTN)ItemOffsetList[Index];\r
216 } else {\r
217 DriverLen = (UINTN)ItemOffsetList[Index + 1] - (UINTN)ItemOffsetList[Index];\r
218 }\r
219\r
220 Status = gBS->LoadImage(\r
221 FALSE,\r
222 gImageHandle,\r
223 DriverDevicePath,\r
224 (UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index],\r
225 DriverLen,\r
226 &ImageHandle\r
227 );\r
228 if (EFI_ERROR(Status)) {\r
229 goto EXIT;\r
230 }\r
231\r
232 Status = gBS->StartImage(\r
233 ImageHandle, \r
234 &ExitDataSize, \r
235 NULL\r
236 );\r
237 if (EFI_ERROR(Status)) {\r
238 DEBUG ((DEBUG_ERROR, "Driver Return Status = %r\n", Status));\r
239 goto EXIT;\r
240 }\r
241 }\r
242\r
243 //\r
244 // Connnect all again to connect drivers within capsule \r
245 //\r
246 if (FmpCapsuleHeader->EmbeddedDriverCount > 0) {\r
247 BdsLibConnectAll();\r
248 }\r
249\r
250 //\r
251 // 3. Route payload to right FMP instance\r
252 //\r
253 Status = gBS->LocateHandleBuffer (\r
254 ByProtocol,\r
255 &gEfiFirmwareManagementProtocolGuid,\r
256 NULL,\r
257 &NumberOfHandles,\r
258 &HandleBuffer\r
259 );\r
260\r
261 if (!EFI_ERROR(Status)) {\r
262 for(Index1 = 0; Index1 < NumberOfHandles; Index1++) {\r
263 Status = gBS->HandleProtocol(\r
264 HandleBuffer[Index1],\r
265 &gEfiFirmwareManagementProtocolGuid,\r
266 (VOID **)&Fmp\r
267 );\r
268 if (EFI_ERROR(Status)) {\r
269 continue;\r
270 }\r
271\r
272 ImageInfoSize = 0;\r
273 Status = Fmp->GetImageInfo (\r
274 Fmp,\r
275 &ImageInfoSize,\r
276 NULL,\r
277 NULL,\r
278 NULL,\r
279 NULL,\r
280 NULL,\r
281 NULL\r
282 );\r
283 if (Status != EFI_BUFFER_TOO_SMALL) {\r
284 continue;\r
285 }\r
286\r
287 FmpImageInfoBuf = NULL;\r
288 FmpImageInfoBuf = AllocateZeroPool (ImageInfoSize);\r
289 if (FmpImageInfoBuf == NULL) {\r
290 Status = EFI_OUT_OF_RESOURCES;\r
291 goto EXIT;\r
292 }\r
293\r
294 PackageVersionName = NULL;\r
295 Status = Fmp->GetImageInfo (\r
296 Fmp,\r
297 &ImageInfoSize, // ImageInfoSize\r
298 FmpImageInfoBuf, // ImageInfo\r
299 &FmpImageInfoDescriptorVer, // DescriptorVersion\r
300 &FmpImageInfoCount, // DescriptorCount\r
301 &DescriptorSize, // DescriptorSize\r
302 &PackageVersion, // PackageVersion\r
303 &PackageVersionName // PackageVersionName\r
304 );\r
305\r
306 //\r
307 // If FMP GetInformation interface failed, skip this resource\r
308 //\r
309 if (EFI_ERROR(Status)) {\r
310 FreePool(FmpImageInfoBuf);\r
311 continue;\r
312 }\r
313\r
314 if (PackageVersionName != NULL) {\r
315 FreePool(PackageVersionName);\r
316 }\r
317\r
318 TempFmpImageInfo = FmpImageInfoBuf;\r
319 for (Index2 = 0; Index2 < FmpImageInfoCount; Index2++) {\r
320 //\r
321 // Check all the payload entry in capsule payload list \r
322 //\r
323 for (Index = FmpCapsuleHeader->EmbeddedDriverCount; Index < ItemNum; Index++) {\r
324 ImageHeader = (EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *)((UINT8 *)FmpCapsuleHeader + ItemOffsetList[Index]);\r
325 if (CompareGuid(&ImageHeader->UpdateImageTypeId, &TempFmpImageInfo->ImageTypeId) &&\r
326 ImageHeader->UpdateImageIndex == TempFmpImageInfo->ImageIndex) {\r
327 AbortReason = NULL;\r
328 if (ImageHeader->UpdateVendorCodeSize == 0) {\r
329 Status = Fmp->SetImage(\r
330 Fmp,\r
331 TempFmpImageInfo->ImageIndex, // ImageIndex\r
332 (UINT8 *)(ImageHeader + 1), // Image\r
333 ImageHeader->UpdateImageSize, // ImageSize\r
334 NULL, // VendorCode\r
335 Update_Image_Progress, // Progress\r
336 &AbortReason // AbortReason\r
337 );\r
338 } else {\r
339 Status = Fmp->SetImage(\r
340 Fmp,\r
341 TempFmpImageInfo->ImageIndex, // ImageIndex\r
342 (UINT8 *)(ImageHeader + 1), // Image\r
343 ImageHeader->UpdateImageSize, // ImageSize\r
344 (UINT8 *)((UINT8 *) (ImageHeader + 1) + ImageHeader->UpdateImageSize), // VendorCode\r
345 Update_Image_Progress, // Progress\r
346 &AbortReason // AbortReason\r
347 );\r
348 }\r
349 if (AbortReason != NULL) {\r
350 DEBUG ((EFI_D_ERROR, "%s\n", AbortReason));\r
351 FreePool(AbortReason);\r
352 }\r
353 }\r
354 }\r
355 //\r
356 // Use DescriptorSize to move ImageInfo Pointer to stay compatible with different ImageInfo version\r
357 //\r
358 TempFmpImageInfo = (EFI_FIRMWARE_IMAGE_DESCRIPTOR *)((UINT8 *)TempFmpImageInfo + DescriptorSize);\r
359 }\r
360 FreePool(FmpImageInfoBuf);\r
361 }\r
362 }\r
363\r
364EXIT:\r
365\r
366 if (HandleBuffer != NULL) {\r
367 FreePool(HandleBuffer);\r
368 }\r
369\r
370 if (DriverDevicePath != NULL) {\r
371 FreePool(DriverDevicePath);\r
372 }\r
373\r
374 return Status;\r
375}\r
376\r
377/**\r
378 Those capsules supported by the firmwares.\r
379\r
380 @param CapsuleHeader Points to a capsule header.\r
381\r
382 @retval EFI_SUCESS Input capsule is supported by firmware.\r
383 @retval EFI_UNSUPPORTED Input capsule is not supported by the firmware.\r
384 @retval EFI_INVALID_PARAMETER Input capsule layout is not correct\r
385**/\r
386EFI_STATUS\r
387EFIAPI\r
388SupportCapsuleImage (\r
389 IN EFI_CAPSULE_HEADER *CapsuleHeader\r
390 )\r
391{\r
392 if (CompareGuid (&gEfiCapsuleGuid, &CapsuleHeader->CapsuleGuid)) {\r
393 return EFI_SUCCESS;\r
394 }\r
395\r
396 if (CompareGuid (&gEfiFmpCapsuleGuid, &CapsuleHeader->CapsuleGuid)) {\r
397 //\r
398 // Check layout of FMP capsule\r
399 //\r
400 return ValidateFmpCapsule(CapsuleHeader);\r
401 }\r
402\r
403 return EFI_UNSUPPORTED;\r
404}\r
405\r
406/**\r
407 The firmware implements to process the capsule image.\r
408\r
409 @param CapsuleHeader Points to a capsule header.\r
410\r
411 @retval EFI_SUCESS Process Capsule Image successfully.\r
412 @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware.\r
413 @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted.\r
414 @retval EFI_OUT_OF_RESOURCES Not enough memory.\r
415**/\r
416EFI_STATUS\r
417EFIAPI\r
418ProcessCapsuleImage (\r
419 IN EFI_CAPSULE_HEADER *CapsuleHeader\r
420 )\r
421{\r
422 UINT32 Length;\r
423 EFI_FIRMWARE_VOLUME_HEADER *FvImage;\r
424 EFI_FIRMWARE_VOLUME_HEADER *ProcessedFvImage;\r
425 EFI_STATUS Status;\r
426 EFI_HANDLE FvProtocolHandle;\r
427 UINT32 FvAlignment;\r
428\r
429 FvImage = NULL;\r
430 ProcessedFvImage = NULL;\r
431 Status = EFI_SUCCESS;\r
432\r
433 if (SupportCapsuleImage (CapsuleHeader) != EFI_SUCCESS) {\r
434 return EFI_UNSUPPORTED;\r
435 }\r
436\r
437 //\r
438 // Check FMP capsule layout\r
439 //\r
440 if (CompareGuid (&gEfiFmpCapsuleGuid, &CapsuleHeader->CapsuleGuid)){\r
441 Status = ValidateFmpCapsule(CapsuleHeader);\r
442 if (EFI_ERROR(Status)) {\r
443 return Status;\r
444 }\r
445\r
446 //\r
447 // Press EFI FMP Capsule\r
448 //\r
449 return ProcessFmpCapsuleImage(CapsuleHeader);\r
450 }\r
451\r
452 //\r
453 // Skip the capsule header, move to the Firware Volume\r
454 //\r
455 FvImage = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINT8 *) CapsuleHeader + CapsuleHeader->HeaderSize);\r
456 Length = CapsuleHeader->CapsuleImageSize - CapsuleHeader->HeaderSize;\r
457\r
458 while (Length != 0) {\r
459 //\r
460 // Point to the next firmware volume header, and then\r
461 // call the DXE service to process it.\r
462 //\r
463 if (FvImage->FvLength > (UINTN) Length) {\r
464 //\r
465 // Notes: need to stuff this status somewhere so that the\r
466 // error can be detected at OS runtime\r
467 //\r
468 Status = EFI_VOLUME_CORRUPTED;\r
469 break;\r
470 }\r
471\r
472 FvAlignment = 1 << ((FvImage->Attributes & EFI_FVB2_ALIGNMENT) >> 16);\r
473 //\r
474 // FvAlignment must be more than 8 bytes required by FvHeader structure.\r
475 //\r
476 if (FvAlignment < 8) {\r
477 FvAlignment = 8;\r
478 }\r
479 //\r
480 // Check FvImage Align is required.\r
481 //\r
482 if (((UINTN) FvImage % FvAlignment) == 0) {\r
483 ProcessedFvImage = FvImage;\r
484 } else {\r
485 //\r
486 // Allocate new aligned buffer to store FvImage.\r
487 //\r
488 ProcessedFvImage = (EFI_FIRMWARE_VOLUME_HEADER *) AllocateAlignedPages ((UINTN) EFI_SIZE_TO_PAGES ((UINTN) FvImage->FvLength), (UINTN) FvAlignment);\r
489 if (ProcessedFvImage == NULL) {\r
490 Status = EFI_OUT_OF_RESOURCES;\r
491 break;\r
492 }\r
493 CopyMem (ProcessedFvImage, FvImage, (UINTN) FvImage->FvLength);\r
494 }\r
495\r
496 Status = gDS->ProcessFirmwareVolume (\r
497 (VOID *) ProcessedFvImage,\r
498 (UINTN) ProcessedFvImage->FvLength,\r
499 &FvProtocolHandle\r
500 );\r
501 if (EFI_ERROR (Status)) {\r
502 break;\r
503 }\r
504 //\r
505 // Call the dispatcher to dispatch any drivers from the produced firmware volume\r
506 //\r
507 gDS->Dispatch ();\r
508 //\r
509 // On to the next FV in the capsule\r
510 //\r
511 Length -= (UINT32) FvImage->FvLength;\r
512 FvImage = (EFI_FIRMWARE_VOLUME_HEADER *) ((UINT8 *) FvImage + FvImage->FvLength);\r
513 }\r
514\r
515 return Status;\r
516}\r
517\r
518/**\r
519\r
520 This routine is called to process capsules.\r
521\r
522 Caution: This function may receive untrusted input.\r
523\r
524 The capsules reported in EFI_HOB_UEFI_CAPSULE are processed.\r
525 If there is no EFI_HOB_UEFI_CAPSULE, this routine does nothing.\r
526\r
527 This routine should be called twice in BDS.\r
528 1) The first call must be before EndOfDxe. The system capsules is processed.\r
529 If device capsule FMP protocols are exposted at this time and device FMP\r
530 capsule has zero EmbeddedDriverCount, the device capsules are processed.\r
531 Each individual capsule result is recorded in capsule record variable.\r
532 System may reset in this function, if reset is required by capsule and\r
533 all capsules are processed.\r
534 If not all capsules are processed, reset will be defered to second call.\r
535\r
536 2) The second call must be after EndOfDxe and after ConnectAll, so that all\r
537 device capsule FMP protocols are exposed.\r
538 The system capsules are skipped. If the device capsules are NOT processed\r
539 in first call, they are processed here.\r
540 Each individual capsule result is recorded in capsule record variable.\r
541 System may reset in this function, if reset is required by capsule\r
542 processed in first call and second call.\r
543\r
544 @retval EFI_SUCCESS There is no error when processing capsules.\r
545 @retval EFI_OUT_OF_RESOURCES No enough resource to process capsules.\r
546\r
547**/\r
548EFI_STATUS\r
549EFIAPI\r
550ProcessCapsules (\r
551 VOID\r
552 )\r
553{\r
554 return EFI_UNSUPPORTED;\r
555}\r
556\r