]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c
d34eb25a6e5d06336415c38402537fc8c4a8487d
[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/CapsuleLib.h>
34
35 #include <IndustryStandard/WindowsUxCapsule.h>
36
37 typedef struct {
38 EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultHeader;
39 EFI_CAPSULE_RESULT_VARIABLE_FMP CapsuleResultFmp;
40 } CAPSULE_RESULT_VARIABLE_CACHE;
41
42 #define CAPSULE_RESULT_VARIABLE_CACHE_COUNT 0x10
43
44 CAPSULE_RESULT_VARIABLE_CACHE *mCapsuleResultVariableCache;
45 UINTN mCapsuleResultVariableCacheMaxCount;
46 UINTN mCapsuleResultVariableCacheCount;
47
48 /**
49 Get current capsule last variable index.
50
51 @return Current capsule last variable index.
52 @retval -1 No current capsule last variable.
53 **/
54 INTN
55 GetCurrentCapsuleLastIndex (
56 VOID
57 )
58 {
59 UINTN Size;
60 CHAR16 CapsuleLastStr[sizeof("Capsule####")];
61 EFI_STATUS Status;
62 UINT16 CurrentIndex;
63
64 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
65 Status = gRT->GetVariable(
66 L"CapsuleLast",
67 &gEfiCapsuleReportGuid,
68 NULL,
69 &Size,
70 CapsuleLastStr
71 );
72 if (EFI_ERROR(Status)) {
73 return -1;
74 }
75 CurrentIndex = (UINT16)StrHexToUintn(&CapsuleLastStr[sizeof("Capsule") - 1]);
76 return CurrentIndex;
77 }
78
79 /**
80 Check if this FMP capsule is processed.
81
82 @param[in] CapsuleHeader The capsule image header
83 @param[in] PayloadIndex FMP payload index
84 @param[in] ImageHeader FMP image header
85
86 @retval TRUE This FMP capsule is processed.
87 @retval FALSE This FMP capsule is not processed.
88 **/
89 BOOLEAN
90 IsFmpCapsuleProcessed (
91 IN EFI_CAPSULE_HEADER *CapsuleHeader,
92 IN UINTN PayloadIndex,
93 IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader
94 )
95 {
96 UINTN Index;
97 EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResult;
98 EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultFmp;
99
100 for (Index = 0; Index < mCapsuleResultVariableCacheCount; Index++) {
101 //
102 // Check
103 //
104 CapsuleResult = &mCapsuleResultVariableCache[Index].CapsuleResultHeader;
105 if (CapsuleResult->VariableTotalSize >= sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER)) {
106 if (CompareGuid(&CapsuleResult->CapsuleGuid, &gEfiFmpCapsuleGuid)) {
107 if (CapsuleResult->VariableTotalSize >= sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) * 2) {
108 CapsuleResultFmp = (EFI_CAPSULE_RESULT_VARIABLE_FMP *)(CapsuleResult + 1);
109 if (CompareGuid(&CapsuleResultFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId) &&
110 (CapsuleResultFmp->UpdateImageIndex == ImageHeader->UpdateImageIndex) &&
111 (CapsuleResultFmp->PayloadIndex == PayloadIndex) ) {
112 return TRUE;
113 }
114 }
115 }
116 }
117 }
118
119 return FALSE;
120 }
121
122 /**
123 Write a new capsule status variable cache.
124
125 @param[in] CapsuleResult The capsule status variable
126 @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes
127
128 @retval EFI_SUCCESS The capsule status variable is cached.
129 @retval EFI_OUT_OF_RESOURCES No resource to cache the capsule status variable.
130 **/
131 EFI_STATUS
132 WriteNewCapsuleResultVariableCache (
133 IN VOID *CapsuleResult,
134 IN UINTN CapsuleResultSize
135 )
136 {
137 if (CapsuleResultSize > sizeof(CAPSULE_RESULT_VARIABLE_CACHE)) {
138 CapsuleResultSize = sizeof(CAPSULE_RESULT_VARIABLE_CACHE);
139 }
140
141 if (mCapsuleResultVariableCacheCount == mCapsuleResultVariableCacheMaxCount) {
142 mCapsuleResultVariableCache = ReallocatePool(
143 mCapsuleResultVariableCacheMaxCount * sizeof(CAPSULE_RESULT_VARIABLE_CACHE),
144 (mCapsuleResultVariableCacheMaxCount + CAPSULE_RESULT_VARIABLE_CACHE_COUNT) * sizeof(CAPSULE_RESULT_VARIABLE_CACHE),
145 mCapsuleResultVariableCache
146 );
147 if (mCapsuleResultVariableCache == NULL) {
148 return EFI_OUT_OF_RESOURCES;
149 }
150 mCapsuleResultVariableCacheMaxCount += CAPSULE_RESULT_VARIABLE_CACHE_COUNT;
151 }
152
153 ASSERT(mCapsuleResultVariableCacheCount < mCapsuleResultVariableCacheMaxCount);
154 ASSERT(mCapsuleResultVariableCache != NULL);
155 CopyMem(
156 &mCapsuleResultVariableCache[mCapsuleResultVariableCacheCount],
157 CapsuleResult,
158 CapsuleResultSize
159 );
160 mCapsuleResultVariableCacheCount++;
161
162 return EFI_SUCCESS;
163 }
164
165 /**
166 Get a new capsule status variable index.
167
168 @return A new capsule status variable index.
169 @retval -1 No new capsule status variable index.
170 **/
171 INTN
172 GetNewCapsuleResultIndex (
173 VOID
174 )
175 {
176 INTN CurrentIndex;
177
178 CurrentIndex = GetCurrentCapsuleLastIndex();
179 if (CurrentIndex >= PcdGet16(PcdCapsuleMax)) {
180 return -1;
181 }
182
183 return CurrentIndex + 1;
184 }
185
186 /**
187 Write a new capsule status variable.
188
189 @param[in] CapsuleResult The capsule status variable
190 @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes
191
192 @retval EFI_SUCCESS The capsule status variable is recorded.
193 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
194 **/
195 EFI_STATUS
196 WriteNewCapsuleResultVariable (
197 IN VOID *CapsuleResult,
198 IN UINTN CapsuleResultSize
199 )
200 {
201 INTN CapsuleResultIndex;
202 CHAR16 CapsuleResultStr[sizeof("Capsule####")];
203 UINTN Size;
204 EFI_STATUS Status;
205
206 CapsuleResultIndex = GetNewCapsuleResultIndex();
207 DEBUG((DEBUG_INFO, "New CapsuleResultIndex - 0x%x\n", CapsuleResultIndex));
208 if (CapsuleResultIndex == -1) {
209 return EFI_OUT_OF_RESOURCES;
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
284 @retval EFI_SUCCESS The capsule status variable is recorded.
285 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
286 **/
287 EFI_STATUS
288 RecordFmpCapsuleStatusVariable (
289 IN EFI_CAPSULE_HEADER *CapsuleHeader,
290 IN EFI_STATUS CapsuleStatus,
291 IN UINTN PayloadIndex,
292 IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader
293 )
294 {
295 EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResultVariableHeader;
296 EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultVariableFmp;
297 EFI_STATUS Status;
298 UINT8 *CapsuleResultVariable;
299 UINT32 CapsuleResultVariableSize;
300
301 CapsuleResultVariable = NULL;
302 //
303 // Allocate zero CHAR16 for CapsuleFileName and CapsuleTarget.
304 //
305 CapsuleResultVariableSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) * 2;
306 CapsuleResultVariable = AllocateZeroPool (CapsuleResultVariableSize);
307 if (CapsuleResultVariable == NULL) {
308 return EFI_OUT_OF_RESOURCES;
309 }
310 CapsuleResultVariableHeader = (VOID *)CapsuleResultVariable;
311 CapsuleResultVariableHeader->VariableTotalSize = CapsuleResultVariableSize;
312 CapsuleResultVariableHeader->Reserved = 0;
313 CopyGuid(&CapsuleResultVariableHeader->CapsuleGuid, &CapsuleHeader->CapsuleGuid);
314 ZeroMem(&CapsuleResultVariableHeader->CapsuleProcessed, sizeof(CapsuleResultVariableHeader->CapsuleProcessed));
315 gRT->GetTime(&CapsuleResultVariableHeader->CapsuleProcessed, NULL);
316 CapsuleResultVariableHeader->CapsuleStatus = CapsuleStatus;
317
318 CapsuleResultVariableFmp = (VOID *)(CapsuleResultVariable + sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER));
319 CapsuleResultVariableFmp->Version = 0x1;
320 CapsuleResultVariableFmp->PayloadIndex = (UINT8)PayloadIndex;
321 CapsuleResultVariableFmp->UpdateImageIndex = ImageHeader->UpdateImageIndex;
322 CopyGuid (&CapsuleResultVariableFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId);
323
324 //
325 // Save Local Cache
326 //
327 Status = WriteNewCapsuleResultVariableCache(CapsuleResultVariable, CapsuleResultVariableSize);
328
329 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
330 Status = WriteNewCapsuleResultVariable(CapsuleResultVariable, CapsuleResultVariableSize);
331 }
332 FreePool (CapsuleResultVariable);
333 return Status;
334 }
335
336 /**
337 Initialize CapsuleMax variables.
338 **/
339 VOID
340 InitCapsuleMaxVariable (
341 VOID
342 )
343 {
344 EFI_STATUS Status;
345 UINTN Size;
346 CHAR16 CapsuleMaxStr[sizeof("Capsule####")];
347 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
348
349 UnicodeSPrint(
350 CapsuleMaxStr,
351 sizeof(CapsuleMaxStr),
352 L"Capsule%04x",
353 PcdGet16(PcdCapsuleMax)
354 );
355
356 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
357 Status = gRT->SetVariable(
358 L"CapsuleMax",
359 &gEfiCapsuleReportGuid,
360 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
361 Size,
362 CapsuleMaxStr
363 );
364 if (!EFI_ERROR(Status)) {
365 // Lock it per UEFI spec.
366 Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
367 if (!EFI_ERROR(Status)) {
368 Status = VariableLock->RequestToLock(VariableLock, L"CapsuleMax", &gEfiCapsuleReportGuid);
369 ASSERT_EFI_ERROR(Status);
370 }
371 }
372 }
373
374 /**
375 Initialize CapsuleLast variables.
376 **/
377 VOID
378 InitCapsuleLastVariable (
379 VOID
380 )
381 {
382 EFI_STATUS Status;
383 EFI_BOOT_MODE BootMode;
384 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
385 VOID *CapsuleResult;
386 UINTN Size;
387 CHAR16 CapsuleLastStr[sizeof("Capsule####")];
388
389 BootMode = GetBootModeHob();
390 if (BootMode == BOOT_ON_FLASH_UPDATE) {
391 Status = gRT->SetVariable(
392 L"CapsuleLast",
393 &gEfiCapsuleReportGuid,
394 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
395 0,
396 NULL
397 );
398 // Do not lock it because it will be updated later.
399 } else {
400 //
401 // Check if OS/APP cleared L"Capsule####"
402 //
403 ZeroMem(CapsuleLastStr, sizeof(CapsuleLastStr));
404 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
405 Status = gRT->GetVariable(
406 L"CapsuleLast",
407 &gEfiCapsuleReportGuid,
408 NULL,
409 &Size,
410 CapsuleLastStr
411 );
412 if (!EFI_ERROR(Status)) {
413 //
414 // L"CapsuleLast" is got, check if data is there.
415 //
416 Status = GetVariable2 (
417 CapsuleLastStr,
418 &gEfiCapsuleReportGuid,
419 (VOID **) &CapsuleResult,
420 NULL
421 );
422 if (EFI_ERROR(Status)) {
423 //
424 // If no data, delete L"CapsuleLast"
425 //
426 Status = gRT->SetVariable(
427 L"CapsuleLast",
428 &gEfiCapsuleReportGuid,
429 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
430 0,
431 NULL
432 );
433 }
434 }
435
436 // Lock it in normal boot path per UEFI spec.
437 Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
438 if (!EFI_ERROR(Status)) {
439 Status = VariableLock->RequestToLock(VariableLock, L"CapsuleLast", &gEfiCapsuleReportGuid);
440 ASSERT_EFI_ERROR(Status);
441 }
442 }
443 }
444
445 /**
446 Initialize capsule update variables.
447 **/
448 VOID
449 InitCapsuleUpdateVariable (
450 VOID
451 )
452 {
453 EFI_STATUS Status;
454 UINTN Index;
455 CHAR16 CapsuleVarName[30];
456 CHAR16 *TempVarName;
457
458 //
459 // Clear all the capsule variables CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
460 // as early as possible which will avoid the next time boot after the capsule update
461 // will still into the capsule loop
462 //
463 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), EFI_CAPSULE_VARIABLE_NAME);
464 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
465 Index = 0;
466 while (TRUE) {
467 if (Index > 0) {
468 UnicodeValueToString (TempVarName, 0, Index, 0);
469 }
470 Status = gRT->SetVariable (
471 CapsuleVarName,
472 &gEfiCapsuleVendorGuid,
473 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
474 0,
475 (VOID *)NULL
476 );
477 if (EFI_ERROR (Status)) {
478 //
479 // There is no capsule variables, quit
480 //
481 break;
482 }
483 Index++;
484 }
485 }
486
487 /**
488 Initialize capsule related variables.
489 **/
490 VOID
491 InitCapsuleVariable (
492 VOID
493 )
494 {
495 InitCapsuleUpdateVariable();
496 InitCapsuleMaxVariable();
497 InitCapsuleLastVariable();
498 //
499 // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast"
500 // to check status and delete them.
501 //
502 }