]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c
MdeModulePkg/CapsuleLib: Support result rolling over.
[mirror_edk2.git] / MdeModulePkg / Library / DxeCapsuleLibFmp / DxeCapsuleReportLib.c
1 /** @file
2 DXE capsule report related function.
3
4 Copyright (c) 2016, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include <PiDxe.h>
16 #include <Protocol/FirmwareManagement.h>
17 #include <Protocol/VariableLock.h>
18 #include <Guid/CapsuleReport.h>
19 #include <Guid/FmpCapsule.h>
20 #include <Guid/CapsuleVendor.h>
21
22 #include <Library/BaseLib.h>
23 #include <Library/DebugLib.h>
24 #include <Library/BaseMemoryLib.h>
25 #include <Library/UefiBootServicesTableLib.h>
26 #include <Library/UefiRuntimeServicesTableLib.h>
27 #include <Library/MemoryAllocationLib.h>
28 #include <Library/UefiLib.h>
29 #include <Library/PcdLib.h>
30 #include <Library/HobLib.h>
31 #include <Library/PrintLib.h>
32 #include <Library/ReportStatusCodeLib.h>
33 #include <Library/DevicePathLib.h>
34 #include <Library/CapsuleLib.h>
35
36 #include <IndustryStandard/WindowsUxCapsule.h>
37
38 typedef struct {
39 EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultHeader;
40 EFI_CAPSULE_RESULT_VARIABLE_FMP CapsuleResultFmp;
41 } CAPSULE_RESULT_VARIABLE_CACHE;
42
43 #define CAPSULE_RESULT_VARIABLE_CACHE_COUNT 0x10
44
45 CAPSULE_RESULT_VARIABLE_CACHE *mCapsuleResultVariableCache;
46 UINTN mCapsuleResultVariableCacheMaxCount;
47 UINTN mCapsuleResultVariableCacheCount;
48
49 /**
50 Get current capsule last variable index.
51
52 @return Current capsule last variable index.
53 @retval -1 No current capsule last variable.
54 **/
55 INTN
56 GetCurrentCapsuleLastIndex (
57 VOID
58 )
59 {
60 UINTN Size;
61 CHAR16 CapsuleLastStr[sizeof("Capsule####")];
62 EFI_STATUS Status;
63 UINT16 CurrentIndex;
64
65 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
66 Status = gRT->GetVariable(
67 L"CapsuleLast",
68 &gEfiCapsuleReportGuid,
69 NULL,
70 &Size,
71 CapsuleLastStr
72 );
73 if (EFI_ERROR(Status)) {
74 return -1;
75 }
76 CurrentIndex = (UINT16)StrHexToUintn(&CapsuleLastStr[sizeof("Capsule") - 1]);
77 return CurrentIndex;
78 }
79
80 /**
81 Check if this FMP capsule is processed.
82
83 @param[in] CapsuleHeader The capsule image header
84 @param[in] PayloadIndex FMP payload index
85 @param[in] ImageHeader FMP image header
86
87 @retval TRUE This FMP capsule is processed.
88 @retval FALSE This FMP capsule is not processed.
89 **/
90 BOOLEAN
91 IsFmpCapsuleProcessed (
92 IN EFI_CAPSULE_HEADER *CapsuleHeader,
93 IN UINTN PayloadIndex,
94 IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader
95 )
96 {
97 UINTN Index;
98 EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResult;
99 EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultFmp;
100
101 for (Index = 0; Index < mCapsuleResultVariableCacheCount; Index++) {
102 //
103 // Check
104 //
105 CapsuleResult = &mCapsuleResultVariableCache[Index].CapsuleResultHeader;
106 if (CapsuleResult->VariableTotalSize >= sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER)) {
107 if (CompareGuid(&CapsuleResult->CapsuleGuid, &gEfiFmpCapsuleGuid)) {
108 if (CapsuleResult->VariableTotalSize >= sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) * 2) {
109 CapsuleResultFmp = (EFI_CAPSULE_RESULT_VARIABLE_FMP *)(CapsuleResult + 1);
110 if (CompareGuid(&CapsuleResultFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId) &&
111 (CapsuleResultFmp->UpdateImageIndex == ImageHeader->UpdateImageIndex) &&
112 (CapsuleResultFmp->PayloadIndex == PayloadIndex) ) {
113 return TRUE;
114 }
115 }
116 }
117 }
118 }
119
120 return FALSE;
121 }
122
123 /**
124 Write a new capsule status variable cache.
125
126 @param[in] CapsuleResult The capsule status variable
127 @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes
128
129 @retval EFI_SUCCESS The capsule status variable is cached.
130 @retval EFI_OUT_OF_RESOURCES No resource to cache the capsule status variable.
131 **/
132 EFI_STATUS
133 WriteNewCapsuleResultVariableCache (
134 IN VOID *CapsuleResult,
135 IN UINTN CapsuleResultSize
136 )
137 {
138 if (CapsuleResultSize > sizeof(CAPSULE_RESULT_VARIABLE_CACHE)) {
139 CapsuleResultSize = sizeof(CAPSULE_RESULT_VARIABLE_CACHE);
140 }
141
142 if (mCapsuleResultVariableCacheCount == mCapsuleResultVariableCacheMaxCount) {
143 mCapsuleResultVariableCache = ReallocatePool(
144 mCapsuleResultVariableCacheMaxCount * sizeof(CAPSULE_RESULT_VARIABLE_CACHE),
145 (mCapsuleResultVariableCacheMaxCount + CAPSULE_RESULT_VARIABLE_CACHE_COUNT) * sizeof(CAPSULE_RESULT_VARIABLE_CACHE),
146 mCapsuleResultVariableCache
147 );
148 if (mCapsuleResultVariableCache == NULL) {
149 return EFI_OUT_OF_RESOURCES;
150 }
151 mCapsuleResultVariableCacheMaxCount += CAPSULE_RESULT_VARIABLE_CACHE_COUNT;
152 }
153
154 ASSERT(mCapsuleResultVariableCacheCount < mCapsuleResultVariableCacheMaxCount);
155 ASSERT(mCapsuleResultVariableCache != NULL);
156 CopyMem(
157 &mCapsuleResultVariableCache[mCapsuleResultVariableCacheCount],
158 CapsuleResult,
159 CapsuleResultSize
160 );
161 mCapsuleResultVariableCacheCount++;
162
163 return EFI_SUCCESS;
164 }
165
166 /**
167 Get a new capsule status variable index.
168
169 @return A new capsule status variable index.
170 @retval 0 No new capsule status variable index. Rolling over.
171 **/
172 INTN
173 GetNewCapsuleResultIndex (
174 VOID
175 )
176 {
177 INTN CurrentIndex;
178
179 CurrentIndex = GetCurrentCapsuleLastIndex();
180 if (CurrentIndex >= PcdGet16(PcdCapsuleMax)) {
181 DEBUG((DEBUG_INFO, " CapsuleResult variable Rolling Over!\n"));
182 return 0;
183 }
184
185 return CurrentIndex + 1;
186 }
187
188 /**
189 Write a new capsule status variable.
190
191 @param[in] CapsuleResult The capsule status variable
192 @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes
193
194 @retval EFI_SUCCESS The capsule status variable is recorded.
195 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
196 **/
197 EFI_STATUS
198 WriteNewCapsuleResultVariable (
199 IN VOID *CapsuleResult,
200 IN UINTN CapsuleResultSize
201 )
202 {
203 INTN CapsuleResultIndex;
204 CHAR16 CapsuleResultStr[sizeof("Capsule####")];
205 UINTN Size;
206 EFI_STATUS Status;
207
208 CapsuleResultIndex = GetNewCapsuleResultIndex();
209 DEBUG((DEBUG_INFO, "New CapsuleResultIndex - 0x%x\n", CapsuleResultIndex));
210
211 UnicodeSPrint(
212 CapsuleResultStr,
213 sizeof(CapsuleResultStr),
214 L"Capsule%04x",
215 CapsuleResultIndex
216 );
217
218 Status = gRT->SetVariable(
219 CapsuleResultStr,
220 &gEfiCapsuleReportGuid,
221 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
222 CapsuleResultSize,
223 CapsuleResult
224 );
225 if (!EFI_ERROR(Status)) {
226 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
227 DEBUG((DEBUG_INFO, "Set CapsuleLast - %s\n", CapsuleResultStr));
228 Status = gRT->SetVariable(
229 L"CapsuleLast",
230 &gEfiCapsuleReportGuid,
231 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
232 Size,
233 CapsuleResultStr
234 );
235 }
236
237 return Status;
238 }
239
240 /**
241 Record capsule status variable and to local cache.
242
243 @param[in] CapsuleHeader The capsule image header
244 @param[in] CapsuleStatus The capsule process stauts
245
246 @retval EFI_SUCCESS The capsule status variable is recorded.
247 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
248 **/
249 EFI_STATUS
250 RecordCapsuleStatusVariable (
251 IN EFI_CAPSULE_HEADER *CapsuleHeader,
252 IN EFI_STATUS CapsuleStatus
253 )
254 {
255 EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultVariable;
256 EFI_STATUS Status;
257
258 CapsuleResultVariable.VariableTotalSize = sizeof(CapsuleResultVariable);
259 CapsuleResultVariable.Reserved = 0;
260 CopyGuid (&CapsuleResultVariable.CapsuleGuid, &CapsuleHeader->CapsuleGuid);
261 ZeroMem(&CapsuleResultVariable.CapsuleProcessed, sizeof(CapsuleResultVariable.CapsuleProcessed));
262 gRT->GetTime(&CapsuleResultVariable.CapsuleProcessed, NULL);
263 CapsuleResultVariable.CapsuleStatus = CapsuleStatus;
264
265 //
266 // Save Local Cache
267 //
268 Status = WriteNewCapsuleResultVariableCache(&CapsuleResultVariable, sizeof(CapsuleResultVariable));
269
270 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
271 Status = WriteNewCapsuleResultVariable(&CapsuleResultVariable, sizeof(CapsuleResultVariable));
272 }
273 return Status;
274 }
275
276 /**
277 Record FMP capsule status variable and to local cache.
278
279 @param[in] CapsuleHeader The capsule image header
280 @param[in] CapsuleStatus The capsule process stauts
281 @param[in] PayloadIndex FMP payload index
282 @param[in] ImageHeader FMP image header
283 @param[in] FmpDevicePath DevicePath associated with the FMP producer
284
285 @retval EFI_SUCCESS The capsule status variable is recorded.
286 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
287 **/
288 EFI_STATUS
289 RecordFmpCapsuleStatusVariable (
290 IN EFI_CAPSULE_HEADER *CapsuleHeader,
291 IN EFI_STATUS CapsuleStatus,
292 IN UINTN PayloadIndex,
293 IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader,
294 IN EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath OPTIONAL
295 )
296 {
297 EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResultVariableHeader;
298 EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultVariableFmp;
299 EFI_STATUS Status;
300 UINT8 *CapsuleResultVariable;
301 UINTN CapsuleResultVariableSize;
302 CHAR16 *DevicePathStr;
303 UINTN DevicePathStrSize;
304
305 DevicePathStr = NULL;
306 if (FmpDevicePath != NULL) {
307 DevicePathStr = ConvertDevicePathToText (FmpDevicePath, FALSE, FALSE);
308 }
309 if (DevicePathStr != NULL) {
310 DevicePathStrSize = StrSize(DevicePathStr);
311 } else {
312 DevicePathStrSize = sizeof(CHAR16);
313 }
314 //
315 // Allocate zero CHAR16 for CapsuleFileName.
316 //
317 CapsuleResultVariableSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) + DevicePathStrSize;
318 CapsuleResultVariable = AllocateZeroPool (CapsuleResultVariableSize);
319 if (CapsuleResultVariable == NULL) {
320 return EFI_OUT_OF_RESOURCES;
321 }
322 CapsuleResultVariableHeader = (VOID *)CapsuleResultVariable;
323 CapsuleResultVariableHeader->VariableTotalSize = (UINT32)CapsuleResultVariableSize;
324 CapsuleResultVariableHeader->Reserved = 0;
325 CopyGuid(&CapsuleResultVariableHeader->CapsuleGuid, &CapsuleHeader->CapsuleGuid);
326 ZeroMem(&CapsuleResultVariableHeader->CapsuleProcessed, sizeof(CapsuleResultVariableHeader->CapsuleProcessed));
327 gRT->GetTime(&CapsuleResultVariableHeader->CapsuleProcessed, NULL);
328 CapsuleResultVariableHeader->CapsuleStatus = CapsuleStatus;
329
330 CapsuleResultVariableFmp = (VOID *)(CapsuleResultVariable + sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER));
331 CapsuleResultVariableFmp->Version = 0x1;
332 CapsuleResultVariableFmp->PayloadIndex = (UINT8)PayloadIndex;
333 CapsuleResultVariableFmp->UpdateImageIndex = ImageHeader->UpdateImageIndex;
334 CopyGuid (&CapsuleResultVariableFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId);
335 if (DevicePathStr != NULL) {
336 CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16), DevicePathStr, DevicePathStrSize);
337 FreePool (DevicePathStr);
338 DevicePathStr = NULL;
339 }
340
341 //
342 // Save Local Cache
343 //
344 Status = WriteNewCapsuleResultVariableCache(CapsuleResultVariable, CapsuleResultVariableSize);
345
346 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
347 Status = WriteNewCapsuleResultVariable(CapsuleResultVariable, CapsuleResultVariableSize);
348 }
349 FreePool (CapsuleResultVariable);
350 return Status;
351 }
352
353 /**
354 Initialize CapsuleMax variables.
355 **/
356 VOID
357 InitCapsuleMaxVariable (
358 VOID
359 )
360 {
361 EFI_STATUS Status;
362 UINTN Size;
363 CHAR16 CapsuleMaxStr[sizeof("Capsule####")];
364 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
365
366 UnicodeSPrint(
367 CapsuleMaxStr,
368 sizeof(CapsuleMaxStr),
369 L"Capsule%04x",
370 PcdGet16(PcdCapsuleMax)
371 );
372
373 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
374 Status = gRT->SetVariable(
375 L"CapsuleMax",
376 &gEfiCapsuleReportGuid,
377 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
378 Size,
379 CapsuleMaxStr
380 );
381 if (!EFI_ERROR(Status)) {
382 // Lock it per UEFI spec.
383 Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
384 if (!EFI_ERROR(Status)) {
385 Status = VariableLock->RequestToLock(VariableLock, L"CapsuleMax", &gEfiCapsuleReportGuid);
386 ASSERT_EFI_ERROR(Status);
387 }
388 }
389 }
390
391 /**
392 Initialize CapsuleLast variables.
393 **/
394 VOID
395 InitCapsuleLastVariable (
396 VOID
397 )
398 {
399 EFI_STATUS Status;
400 EFI_BOOT_MODE BootMode;
401 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
402 VOID *CapsuleResult;
403 UINTN Size;
404 CHAR16 CapsuleLastStr[sizeof("Capsule####")];
405
406 BootMode = GetBootModeHob();
407 if (BootMode == BOOT_ON_FLASH_UPDATE) {
408 Status = gRT->SetVariable(
409 L"CapsuleLast",
410 &gEfiCapsuleReportGuid,
411 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
412 0,
413 NULL
414 );
415 // Do not lock it because it will be updated later.
416 } else {
417 //
418 // Check if OS/APP cleared L"Capsule####"
419 //
420 ZeroMem(CapsuleLastStr, sizeof(CapsuleLastStr));
421 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
422 Status = gRT->GetVariable(
423 L"CapsuleLast",
424 &gEfiCapsuleReportGuid,
425 NULL,
426 &Size,
427 CapsuleLastStr
428 );
429 if (!EFI_ERROR(Status)) {
430 //
431 // L"CapsuleLast" is got, check if data is there.
432 //
433 Status = GetVariable2 (
434 CapsuleLastStr,
435 &gEfiCapsuleReportGuid,
436 (VOID **) &CapsuleResult,
437 NULL
438 );
439 if (EFI_ERROR(Status)) {
440 //
441 // If no data, delete L"CapsuleLast"
442 //
443 Status = gRT->SetVariable(
444 L"CapsuleLast",
445 &gEfiCapsuleReportGuid,
446 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
447 0,
448 NULL
449 );
450 }
451 }
452
453 // Lock it in normal boot path per UEFI spec.
454 Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
455 if (!EFI_ERROR(Status)) {
456 Status = VariableLock->RequestToLock(VariableLock, L"CapsuleLast", &gEfiCapsuleReportGuid);
457 ASSERT_EFI_ERROR(Status);
458 }
459 }
460 }
461
462 /**
463 Initialize capsule update variables.
464 **/
465 VOID
466 InitCapsuleUpdateVariable (
467 VOID
468 )
469 {
470 EFI_STATUS Status;
471 UINTN Index;
472 CHAR16 CapsuleVarName[30];
473 CHAR16 *TempVarName;
474
475 //
476 // Clear all the capsule variables CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
477 // as early as possible which will avoid the next time boot after the capsule update
478 // will still into the capsule loop
479 //
480 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), EFI_CAPSULE_VARIABLE_NAME);
481 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
482 Index = 0;
483 while (TRUE) {
484 if (Index > 0) {
485 UnicodeValueToString (TempVarName, 0, Index, 0);
486 }
487 Status = gRT->SetVariable (
488 CapsuleVarName,
489 &gEfiCapsuleVendorGuid,
490 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
491 0,
492 (VOID *)NULL
493 );
494 if (EFI_ERROR (Status)) {
495 //
496 // There is no capsule variables, quit
497 //
498 break;
499 }
500 Index++;
501 }
502 }
503
504 /**
505 Initialize capsule related variables.
506 **/
507 VOID
508 InitCapsuleVariable (
509 VOID
510 )
511 {
512 InitCapsuleUpdateVariable();
513 InitCapsuleMaxVariable();
514 InitCapsuleLastVariable();
515 //
516 // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast"
517 // to check status and delete them.
518 //
519 }