1 /** @file
2 DXE capsule process.
3
4 Caution: This module requires additional review when modified.
5 This module will have external input - capsule image.
6 This external input must be validated carefully to avoid security issue like
7 buffer overflow, integer overflow.
8
9 ProcessCapsules(), ProcessTheseCapsules() will receive untrusted
10 input and do basic validation.
11
12 Copyright (c) 2016 - 2018, Intel Corporation. All rights reserved.<BR>
13 SPDX-License-Identifier: BSD-2-Clause-Patent
14
15 **/
16
17 #include <PiDxe.h>
18 #include <Protocol/EsrtManagement.h>
19 #include <Protocol/FirmwareManagementProgress.h>
20
21 #include <Library/BaseLib.h>
22 #include <Library/DebugLib.h>
23 #include <Library/BaseMemoryLib.h>
24 #include <Library/UefiBootServicesTableLib.h>
25 #include <Library/UefiRuntimeServicesTableLib.h>
26 #include <Library/MemoryAllocationLib.h>
27 #include <Library/UefiLib.h>
28 #include <Library/PcdLib.h>
29 #include <Library/HobLib.h>
30 #include <Library/ReportStatusCodeLib.h>
31 #include <Library/CapsuleLib.h>
32 #include <Library/DisplayUpdateProgressLib.h>
33
34 #include <IndustryStandard/WindowsUxCapsule.h>
35
36 extern EDKII_FIRMWARE_MANAGEMENT_PROGRESS_PROTOCOL *mFmpProgress;
37
38 /**
39 Return if this FMP is a system FMP or a device FMP, based upon CapsuleHeader.
40
41 @param[in] CapsuleHeader A pointer to EFI_CAPSULE_HEADER
42
43 @retval TRUE It is a system FMP.
44 @retval FALSE It is a device FMP.
45 **/
46 BOOLEAN
47 IsFmpCapsule (
48 IN EFI_CAPSULE_HEADER *CapsuleHeader
49 );
50
51 /**
52 Validate Fmp capsules layout.
53
54 Caution: This function may receive untrusted input.
55
56 This function assumes the caller validated the capsule by using
57 IsValidCapsuleHeader(), so that all fields in EFI_CAPSULE_HEADER are correct.
58 The capsule buffer size is CapsuleHeader->CapsuleImageSize.
59
60 This function validates the fields in EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER
61 and EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER.
62
63 This function need support nested FMP capsule.
64
65 @param[in] CapsuleHeader Points to a capsule header.
66 @param[out] EmbeddedDriverCount The EmbeddedDriverCount in the FMP capsule.
67
68 @retval EFI_SUCESS Input capsule is a correct FMP capsule.
69 @retval EFI_INVALID_PARAMETER Input capsule is not a correct FMP capsule.
70 **/
71 EFI_STATUS
72 ValidateFmpCapsule (
73 IN EFI_CAPSULE_HEADER *CapsuleHeader,
74 OUT UINT16 *EmbeddedDriverCount OPTIONAL
75 );
76
77 /**
78 Validate if it is valid capsule header
79
80 This function assumes the caller provided correct CapsuleHeader pointer
81 and CapsuleSize.
82
83 This function validates the fields in EFI_CAPSULE_HEADER.
84
85 @param[in] CapsuleHeader Points to a capsule header.
86 @param[in] CapsuleSize Size of the whole capsule image.
87
88 **/
89 BOOLEAN
90 IsValidCapsuleHeader (
91 IN EFI_CAPSULE_HEADER *CapsuleHeader,
92 IN UINT64 CapsuleSize
93 );
94
95 extern BOOLEAN mDxeCapsuleLibEndOfDxe;
96 BOOLEAN mNeedReset = FALSE;
97
98 VOID **mCapsulePtr;
99 EFI_STATUS *mCapsuleStatusArray;
100 UINT32 mCapsuleTotalNumber;
101
102 /**
103 The firmware implements to process the capsule image.
104
105 Caution: This function may receive untrusted input.
106
107 @param[in] CapsuleHeader Points to a capsule header.
108 @param[out] ResetRequired Indicates whether reset is required or not.
109
110 @retval EFI_SUCESS Process Capsule Image successfully.
111 @retval EFI_UNSUPPORTED Capsule image is not supported by the firmware.
112 @retval EFI_VOLUME_CORRUPTED FV volume in the capsule is corrupted.
113 @retval EFI_OUT_OF_RESOURCES Not enough memory.
114 **/
115 EFI_STATUS
116 EFIAPI
117 ProcessThisCapsuleImage (
118 IN EFI_CAPSULE_HEADER *CapsuleHeader,
119 OUT BOOLEAN *ResetRequired OPTIONAL
120 );
121
122 /**
123 Function indicate the current completion progress of the firmware
124 update. Platform may override with own specific progress function.
125
126 @param[in] Completion A value between 1 and 100 indicating the current
127 completion progress of the firmware update
128
129 @retval EFI_SUCESS The capsule update progress was updated.
130 @retval EFI_INVALID_PARAMETER Completion is greater than 100%.
131 **/
132 EFI_STATUS
133 EFIAPI
UpdateImageProgress(IN UINTN Completion)134 UpdateImageProgress (
135 IN UINTN Completion
136 )
137 {
138 EFI_STATUS Status;
139 UINTN Seconds;
140 EFI_GRAPHICS_OUTPUT_BLT_PIXEL_UNION *Color;
141
142 DEBUG((DEBUG_INFO, "Update Progress - %d%%\n", Completion));
143
144 if (Completion > 100) {
145 return EFI_INVALID_PARAMETER;
146 }
147
148 //
149 // Use a default timeout of 5 minutes if there is not FMP Progress Protocol.
150 //
151 Seconds = 5 * 60;
152 Color = NULL;
153 if (mFmpProgress != NULL) {
154 Seconds = mFmpProgress->WatchdogSeconds;
155 Color = &mFmpProgress->ProgressBarForegroundColor;
156 }
157
158 //
159 // Cancel the watchdog timer
160 //
161 gBS->SetWatchdogTimer (0, 0x0000, 0, NULL);
162
163 if (Completion != 100) {
164 //
165 // Arm the watchdog timer from PCD setting
166 //
167 if (Seconds != 0) {
168 DEBUG ((DEBUG_VERBOSE, "Arm watchdog timer %d seconds\n", Seconds));
169 gBS->SetWatchdogTimer (Seconds, 0x0000, 0, NULL);
170 }
171 }
172
173 Status = DisplayUpdateProgress (Completion, Color);
174
175 return Status;
176 }
177
178 /**
179 This function initializes the mCapsulePtr, mCapsuleStatusArray and mCapsuleTotalNumber.
180 **/
181 VOID
InitCapsulePtr(VOID)182 InitCapsulePtr (
183 VOID
184 )
185 {
186 EFI_PEI_HOB_POINTERS HobPointer;
187 UINTN Index;
188
189 //
190 // Find all capsule images from hob
191 //
192 HobPointer.Raw = GetHobList ();
193 while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) {
194 if (!IsValidCapsuleHeader((VOID *)(UINTN)HobPointer.Capsule->BaseAddress, HobPointer.Capsule->Length)) {
195 HobPointer.Header->HobType = EFI_HOB_TYPE_UNUSED; // Mark this hob as invalid
196 } else {
197 mCapsuleTotalNumber++;
198 }
199 HobPointer.Raw = GET_NEXT_HOB (HobPointer);
200 }
201
202 DEBUG ((DEBUG_INFO, "mCapsuleTotalNumber - 0x%x\n", mCapsuleTotalNumber));
203
204 if (mCapsuleTotalNumber == 0) {
205 return ;
206 }
207
208 //
209 // Init temp Capsule Data table.
210 //
211 mCapsulePtr = (VOID **) AllocateZeroPool (sizeof (VOID *) * mCapsuleTotalNumber);
212 if (mCapsulePtr == NULL) {
213 DEBUG ((DEBUG_ERROR, "Allocate mCapsulePtr fail!\n"));
214 mCapsuleTotalNumber = 0;
215 return ;
216 }
217 mCapsuleStatusArray = (EFI_STATUS *) AllocateZeroPool (sizeof (EFI_STATUS) * mCapsuleTotalNumber);
218 if (mCapsuleStatusArray == NULL) {
219 DEBUG ((DEBUG_ERROR, "Allocate mCapsuleStatusArray fail!\n"));
220 FreePool (mCapsulePtr);
221 mCapsulePtr = NULL;
222 mCapsuleTotalNumber = 0;
223 return ;
224 }
225 SetMemN (mCapsuleStatusArray, sizeof (EFI_STATUS) * mCapsuleTotalNumber, EFI_NOT_READY);
226
227 //
228 // Find all capsule images from hob
229 //
230 HobPointer.Raw = GetHobList ();
231 Index = 0;
232 while ((HobPointer.Raw = GetNextHob (EFI_HOB_TYPE_UEFI_CAPSULE, HobPointer.Raw)) != NULL) {
233 mCapsulePtr [Index++] = (VOID *) (UINTN) HobPointer.Capsule->BaseAddress;
234 HobPointer.Raw = GET_NEXT_HOB (HobPointer);
235 }
236 }
237
238 /**
239 This function returns if all capsule images are processed.
240
241 @retval TRUE All capsule images are processed.
242 @retval FALSE Not all capsule images are processed.
243 **/
244 BOOLEAN
AreAllImagesProcessed(VOID)245 AreAllImagesProcessed (
246 VOID
247 )
248 {
249 UINTN Index;
250
251 for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
252 if (mCapsuleStatusArray[Index] == EFI_NOT_READY) {
253 return FALSE;
254 }
255 }
256
257 return TRUE;
258 }
259
260 /**
261 This function populates capsule in the configuration table.
262 **/
263 VOID
PopulateCapsuleInConfigurationTable(VOID)264 PopulateCapsuleInConfigurationTable (
265 VOID
266 )
267 {
268 VOID **CapsulePtrCache;
269 EFI_GUID *CapsuleGuidCache;
270 EFI_CAPSULE_HEADER *CapsuleHeader;
271 EFI_CAPSULE_TABLE *CapsuleTable;
272 UINT32 CacheIndex;
273 UINT32 CacheNumber;
274 UINT32 CapsuleNumber;
275 UINTN Index;
276 UINTN Size;
277 EFI_STATUS Status;
278
279 if (mCapsuleTotalNumber == 0) {
280 return ;
281 }
282
283 CapsulePtrCache = NULL;
284 CapsuleGuidCache = NULL;
285 CacheIndex = 0;
286 CacheNumber = 0;
287
288 CapsulePtrCache = (VOID **) AllocateZeroPool (sizeof (VOID *) * mCapsuleTotalNumber);
289 if (CapsulePtrCache == NULL) {
290 DEBUG ((DEBUG_ERROR, "Allocate CapsulePtrCache fail!\n"));
291 return ;
292 }
293 CapsuleGuidCache = (EFI_GUID *) AllocateZeroPool (sizeof (EFI_GUID) * mCapsuleTotalNumber);
294 if (CapsuleGuidCache == NULL) {
295 DEBUG ((DEBUG_ERROR, "Allocate CapsuleGuidCache fail!\n"));
296 FreePool (CapsulePtrCache);
297 return ;
298 }
299
300 //
301 // Capsules who have CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE always are used for operating
302 // System to have information persist across a system reset. EFI System Table must
303 // point to an array of capsules that contains the same CapsuleGuid value. And agents
304 // searching for this type capsule will look in EFI System Table and search for the
305 // capsule's Guid and associated pointer to retrieve the data. Two steps below describes
306 // how to sorting the capsules by the unique guid and install the array to EFI System Table.
307 // Firstly, Loop for all coalesced capsules, record unique CapsuleGuids and cache them in an
308 // array for later sorting capsules by CapsuleGuid.
309 //
310 for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
311 CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
312 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) {
313 //
314 // For each capsule, we compare it with known CapsuleGuid in the CacheArray.
315 // If already has the Guid, skip it. Whereas, record it in the CacheArray as
316 // an additional one.
317 //
318 CacheIndex = 0;
319 while (CacheIndex < CacheNumber) {
320 if (CompareGuid(&CapsuleGuidCache[CacheIndex],&CapsuleHeader->CapsuleGuid)) {
321 break;
322 }
323 CacheIndex++;
324 }
325 if (CacheIndex == CacheNumber) {
326 CopyMem(&CapsuleGuidCache[CacheNumber++],&CapsuleHeader->CapsuleGuid,sizeof(EFI_GUID));
327 }
328 }
329 }
330
331 //
332 // Secondly, for each unique CapsuleGuid in CacheArray, gather all coalesced capsules
333 // whose guid is the same as it, and malloc memory for an array which preceding
334 // with UINT32. The array fills with entry point of capsules that have the same
335 // CapsuleGuid, and UINT32 represents the size of the array of capsules. Then install
336 // this array into EFI System Table, so that agents searching for this type capsule
337 // will look in EFI System Table and search for the capsule's Guid and associated
338 // pointer to retrieve the data.
339 //
340 for (CacheIndex = 0; CacheIndex < CacheNumber; CacheIndex++) {
341 CapsuleNumber = 0;
342 for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
343 CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
344 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE) != 0) {
345 if (CompareGuid (&CapsuleGuidCache[CacheIndex], &CapsuleHeader->CapsuleGuid)) {
346 //
347 // Cache Caspuleheader to the array, this array is uniqued with certain CapsuleGuid.
348 //
349 CapsulePtrCache[CapsuleNumber++] = (VOID*)CapsuleHeader;
350 }
351 }
352 }
353 if (CapsuleNumber != 0) {
354 Size = sizeof(EFI_CAPSULE_TABLE) + (CapsuleNumber - 1) * sizeof(VOID*);
355 CapsuleTable = AllocateRuntimePool (Size);
356 if (CapsuleTable == NULL) {
357 DEBUG ((DEBUG_ERROR, "Allocate CapsuleTable (%g) fail!\n", &CapsuleGuidCache[CacheIndex]));
358 continue;
359 }
360 CapsuleTable->CapsuleArrayNumber = CapsuleNumber;
361 CopyMem(&CapsuleTable->CapsulePtr[0], CapsulePtrCache, CapsuleNumber * sizeof(VOID*));
362 Status = gBS->InstallConfigurationTable (&CapsuleGuidCache[CacheIndex], (VOID*)CapsuleTable);
363 if (EFI_ERROR (Status)) {
364 DEBUG ((DEBUG_ERROR, "InstallConfigurationTable (%g) fail!\n", &CapsuleGuidCache[CacheIndex]));
365 }
366 }
367 }
368
369 FreePool(CapsuleGuidCache);
370 FreePool(CapsulePtrCache);
371 }
372
373 /**
374
375 This routine is called to process capsules.
376
377 Caution: This function may receive untrusted input.
378
379 Each individual capsule result is recorded in capsule record variable.
380
381 @param[in] FirstRound TRUE: First round. Need skip the FMP capsules with non zero EmbeddedDriverCount.
382 FALSE: Process rest FMP capsules.
383
384 @retval EFI_SUCCESS There is no error when processing capsules.
385 @retval EFI_OUT_OF_RESOURCES No enough resource to process capsules.
386
387 **/
388 EFI_STATUS
ProcessTheseCapsules(IN BOOLEAN FirstRound)389 ProcessTheseCapsules (
390 IN BOOLEAN FirstRound
391 )
392 {
393 EFI_STATUS Status;
394 EFI_CAPSULE_HEADER *CapsuleHeader;
395 UINT32 Index;
396 ESRT_MANAGEMENT_PROTOCOL *EsrtManagement;
397 UINT16 EmbeddedDriverCount;
398 BOOLEAN ResetRequired;
399
400 REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesBegin)));
401
402 if (FirstRound) {
403 InitCapsulePtr ();
404 }
405
406 if (mCapsuleTotalNumber == 0) {
407 //
408 // We didn't find a hob, so had no errors.
409 //
410 DEBUG ((DEBUG_ERROR, "We can not find capsule data in capsule update boot mode.\n"));
411 return EFI_SUCCESS;
412 }
413
414 if (AreAllImagesProcessed ()) {
415 return EFI_SUCCESS;
416 }
417
418 //
419 // Check the capsule flags,if contains CAPSULE_FLAGS_POPULATE_SYSTEM_TABLE, install
420 // capsuleTable to configure table with EFI_CAPSULE_GUID
421 //
422 if (FirstRound) {
423 PopulateCapsuleInConfigurationTable ();
424 }
425
426 REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdatingFirmware)));
427
428 //
429 // If Windows UX capsule exist, process it first
430 //
431 for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
432 CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
433 if (CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {
434 DEBUG ((DEBUG_INFO, "ProcessThisCapsuleImage (Ux) - 0x%x\n", CapsuleHeader));
435 DEBUG ((DEBUG_INFO, "Display logo capsule is found.\n"));
436 Status = ProcessThisCapsuleImage (CapsuleHeader, NULL);
437 mCapsuleStatusArray [Index] = EFI_SUCCESS;
438 DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage (Ux) - %r\n", Status));
439 break;
440 }
441 }
442
443 DEBUG ((DEBUG_INFO, "Updating the firmware ......\n"));
444
445 //
446 // All capsules left are recognized by platform.
447 //
448 for (Index = 0; Index < mCapsuleTotalNumber; Index++) {
449 if (mCapsuleStatusArray [Index] != EFI_NOT_READY) {
450 // already processed
451 continue;
452 }
453 CapsuleHeader = (EFI_CAPSULE_HEADER*) mCapsulePtr [Index];
454 if (!CompareGuid (&CapsuleHeader->CapsuleGuid, &gWindowsUxCapsuleGuid)) {
455 //
456 // Call capsule library to process capsule image.
457 //
458 EmbeddedDriverCount = 0;
459 if (IsFmpCapsule(CapsuleHeader)) {
460 Status = ValidateFmpCapsule (CapsuleHeader, &EmbeddedDriverCount);
461 if (EFI_ERROR(Status)) {
462 DEBUG((DEBUG_ERROR, "ValidateFmpCapsule failed. Ignore!\n"));
463 mCapsuleStatusArray [Index] = EFI_ABORTED;
464 continue;
465 }
466 } else {
467 mCapsuleStatusArray [Index] = EFI_ABORTED;
468 continue;
469 }
470
471 if ((!FirstRound) || (EmbeddedDriverCount == 0)) {
472 DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage - 0x%x\n", CapsuleHeader));
473 ResetRequired = FALSE;
474 Status = ProcessThisCapsuleImage (CapsuleHeader, &ResetRequired);
475 mCapsuleStatusArray [Index] = Status;
476 DEBUG((DEBUG_INFO, "ProcessThisCapsuleImage - %r\n", Status));
477
478 if (Status != EFI_NOT_READY) {
479 if (EFI_ERROR(Status)) {
480 REPORT_STATUS_CODE(EFI_ERROR_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdateFirmwareFailed)));
481 DEBUG ((DEBUG_ERROR, "Capsule process failed!\n"));
482 } else {
483 REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeUpdateFirmwareSuccess)));
484 }
485
486 mNeedReset |= ResetRequired;
487 if ((CapsuleHeader->Flags & PcdGet16(PcdSystemRebootAfterCapsuleProcessFlag)) != 0) {
488 mNeedReset = TRUE;
489 }
490 }
491 }
492 }
493 }
494
495 Status = gBS->LocateProtocol(&gEsrtManagementProtocolGuid, NULL, (VOID **)&EsrtManagement);
496 //
497 // Always sync ESRT Cache from FMP Instance
498 //
499 if (!EFI_ERROR(Status)) {
500 EsrtManagement->SyncEsrtFmp();
501 }
502 Status = EFI_SUCCESS;
503
504 REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeProcessCapsulesEnd)));
505
506 return Status;
507 }
508
509 /**
510 Do reset system.
511 **/
512 VOID
DoResetSystem(VOID)513 DoResetSystem (
514 VOID
515 )
516 {
517 DEBUG((DEBUG_INFO, "Capsule Request Cold Reboot."));
518
519 REPORT_STATUS_CODE(EFI_PROGRESS_CODE, (EFI_SOFTWARE | PcdGet32(PcdStatusCodeSubClassCapsule) | PcdGet32(PcdCapsuleStatusCodeResettingSystem)));
520
521 gRT->ResetSystem(EfiResetCold, EFI_SUCCESS, 0, NULL);
522
523 CpuDeadLoop();
524 }
525
526 /**
527
528 This routine is called to process capsules.
529
530 Caution: This function may receive untrusted input.
531
532 The capsules reported in EFI_HOB_UEFI_CAPSULE are processed.
533 If there is no EFI_HOB_UEFI_CAPSULE, this routine does nothing.
534
535 This routine should be called twice in BDS.
536 1) The first call must be before EndOfDxe. The system capsules is processed.
537 If device capsule FMP protocols are exposted at this time and device FMP
538 capsule has zero EmbeddedDriverCount, the device capsules are processed.
539 Each individual capsule result is recorded in capsule record variable.
540 System may reset in this function, if reset is required by capsule and
541 all capsules are processed.
542 If not all capsules are processed, reset will be defered to second call.
543
544 2) The second call must be after EndOfDxe and after ConnectAll, so that all
545 device capsule FMP protocols are exposed.
546 The system capsules are skipped. If the device capsules are NOT processed
547 in first call, they are processed here.
548 Each individual capsule result is recorded in capsule record variable.
549 System may reset in this function, if reset is required by capsule
550 processed in first call and second call.
551
552 @retval EFI_SUCCESS There is no error when processing capsules.
553 @retval EFI_OUT_OF_RESOURCES No enough resource to process capsules.
554
555 **/
556 EFI_STATUS
557 EFIAPI
ProcessCapsules(VOID)558 ProcessCapsules (
559 VOID
560 )
561 {
562 EFI_STATUS Status;
563
564 if (!mDxeCapsuleLibEndOfDxe) {
565 Status = ProcessTheseCapsules(TRUE);
566
567 //
568 // Reboot System if and only if all capsule processed.
569 // If not, defer reset to 2nd process.
570 //
571 if (mNeedReset && AreAllImagesProcessed()) {
572 DoResetSystem();
573 }
574 } else {
575 Status = ProcessTheseCapsules(FALSE);
576 //
577 // Reboot System if required after all capsule processed
578 //
579 if (mNeedReset) {
580 DoResetSystem();
581 }
582 }
583 return Status;
584 }
585