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