]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c
MdeModulePkg/CapsuleLib: Add CapsuleTarget support.
[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 -1 No new capsule status variable index.
171 **/
172 INTN
173 GetNewCapsuleResultIndex (
174 VOID
175 )
176 {
177 INTN CurrentIndex;
178
179 CurrentIndex = GetCurrentCapsuleLastIndex();
180 if (CurrentIndex >= PcdGet16(PcdCapsuleMax)) {
181 return -1;
182 }
183
184 return CurrentIndex + 1;
185 }
186
187 /**
188 Write a new capsule status variable.
189
190 @param[in] CapsuleResult The capsule status variable
191 @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes
192
193 @retval EFI_SUCCESS The capsule status variable is recorded.
194 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
195 **/
196 EFI_STATUS
197 WriteNewCapsuleResultVariable (
198 IN VOID *CapsuleResult,
199 IN UINTN CapsuleResultSize
200 )
201 {
202 INTN CapsuleResultIndex;
203 CHAR16 CapsuleResultStr[sizeof("Capsule####")];
204 UINTN Size;
205 EFI_STATUS Status;
206
207 CapsuleResultIndex = GetNewCapsuleResultIndex();
208 DEBUG((DEBUG_INFO, "New CapsuleResultIndex - 0x%x\n", CapsuleResultIndex));
209 if (CapsuleResultIndex == -1) {
210 return EFI_OUT_OF_RESOURCES;
211 }
212 UnicodeSPrint(
213 CapsuleResultStr,
214 sizeof(CapsuleResultStr),
215 L"Capsule%04x",
216 CapsuleResultIndex
217 );
218
219 Status = gRT->SetVariable(
220 CapsuleResultStr,
221 &gEfiCapsuleReportGuid,
222 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
223 CapsuleResultSize,
224 CapsuleResult
225 );
226 if (!EFI_ERROR(Status)) {
227 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
228 DEBUG((DEBUG_INFO, "Set CapsuleLast - %s\n", CapsuleResultStr));
229 Status = gRT->SetVariable(
230 L"CapsuleLast",
231 &gEfiCapsuleReportGuid,
232 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
233 Size,
234 CapsuleResultStr
235 );
236 }
237
238 return Status;
239 }
240
241 /**
242 Record capsule status variable and to local cache.
243
244 @param[in] CapsuleHeader The capsule image header
245 @param[in] CapsuleStatus The capsule process stauts
246
247 @retval EFI_SUCCESS The capsule status variable is recorded.
248 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
249 **/
250 EFI_STATUS
251 RecordCapsuleStatusVariable (
252 IN EFI_CAPSULE_HEADER *CapsuleHeader,
253 IN EFI_STATUS CapsuleStatus
254 )
255 {
256 EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultVariable;
257 EFI_STATUS Status;
258
259 CapsuleResultVariable.VariableTotalSize = sizeof(CapsuleResultVariable);
260 CapsuleResultVariable.Reserved = 0;
261 CopyGuid (&CapsuleResultVariable.CapsuleGuid, &CapsuleHeader->CapsuleGuid);
262 ZeroMem(&CapsuleResultVariable.CapsuleProcessed, sizeof(CapsuleResultVariable.CapsuleProcessed));
263 gRT->GetTime(&CapsuleResultVariable.CapsuleProcessed, NULL);
264 CapsuleResultVariable.CapsuleStatus = CapsuleStatus;
265
266 //
267 // Save Local Cache
268 //
269 Status = WriteNewCapsuleResultVariableCache(&CapsuleResultVariable, sizeof(CapsuleResultVariable));
270
271 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
272 Status = WriteNewCapsuleResultVariable(&CapsuleResultVariable, sizeof(CapsuleResultVariable));
273 }
274 return Status;
275 }
276
277 /**
278 Record FMP capsule status variable and to local cache.
279
280 @param[in] CapsuleHeader The capsule image header
281 @param[in] CapsuleStatus The capsule process stauts
282 @param[in] PayloadIndex FMP payload index
283 @param[in] ImageHeader FMP image header
284 @param[in] FmpDevicePath DevicePath associated with the FMP producer
285
286 @retval EFI_SUCCESS The capsule status variable is recorded.
287 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
288 **/
289 EFI_STATUS
290 RecordFmpCapsuleStatusVariable (
291 IN EFI_CAPSULE_HEADER *CapsuleHeader,
292 IN EFI_STATUS CapsuleStatus,
293 IN UINTN PayloadIndex,
294 IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader,
295 IN EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath OPTIONAL
296 )
297 {
298 EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResultVariableHeader;
299 EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultVariableFmp;
300 EFI_STATUS Status;
301 UINT8 *CapsuleResultVariable;
302 UINTN CapsuleResultVariableSize;
303 CHAR16 *DevicePathStr;
304 UINTN DevicePathStrSize;
305
306 DevicePathStr = NULL;
307 if (FmpDevicePath != NULL) {
308 DevicePathStr = ConvertDevicePathToText (FmpDevicePath, FALSE, FALSE);
309 }
310 if (DevicePathStr != NULL) {
311 DevicePathStrSize = StrSize(DevicePathStr);
312 } else {
313 DevicePathStrSize = sizeof(CHAR16);
314 }
315 //
316 // Allocate zero CHAR16 for CapsuleFileName.
317 //
318 CapsuleResultVariableSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) + DevicePathStrSize;
319 CapsuleResultVariable = AllocateZeroPool (CapsuleResultVariableSize);
320 if (CapsuleResultVariable == NULL) {
321 return EFI_OUT_OF_RESOURCES;
322 }
323 CapsuleResultVariableHeader = (VOID *)CapsuleResultVariable;
324 CapsuleResultVariableHeader->VariableTotalSize = (UINT32)CapsuleResultVariableSize;
325 CapsuleResultVariableHeader->Reserved = 0;
326 CopyGuid(&CapsuleResultVariableHeader->CapsuleGuid, &CapsuleHeader->CapsuleGuid);
327 ZeroMem(&CapsuleResultVariableHeader->CapsuleProcessed, sizeof(CapsuleResultVariableHeader->CapsuleProcessed));
328 gRT->GetTime(&CapsuleResultVariableHeader->CapsuleProcessed, NULL);
329 CapsuleResultVariableHeader->CapsuleStatus = CapsuleStatus;
330
331 CapsuleResultVariableFmp = (VOID *)(CapsuleResultVariable + sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER));
332 CapsuleResultVariableFmp->Version = 0x1;
333 CapsuleResultVariableFmp->PayloadIndex = (UINT8)PayloadIndex;
334 CapsuleResultVariableFmp->UpdateImageIndex = ImageHeader->UpdateImageIndex;
335 CopyGuid (&CapsuleResultVariableFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId);
336 if (DevicePathStr != NULL) {
337 CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16), DevicePathStr, DevicePathStrSize);
338 FreePool (DevicePathStr);
339 DevicePathStr = NULL;
340 }
341
342 //
343 // Save Local Cache
344 //
345 Status = WriteNewCapsuleResultVariableCache(CapsuleResultVariable, CapsuleResultVariableSize);
346
347 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
348 Status = WriteNewCapsuleResultVariable(CapsuleResultVariable, CapsuleResultVariableSize);
349 }
350 FreePool (CapsuleResultVariable);
351 return Status;
352 }
353
354 /**
355 Initialize CapsuleMax variables.
356 **/
357 VOID
358 InitCapsuleMaxVariable (
359 VOID
360 )
361 {
362 EFI_STATUS Status;
363 UINTN Size;
364 CHAR16 CapsuleMaxStr[sizeof("Capsule####")];
365 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
366
367 UnicodeSPrint(
368 CapsuleMaxStr,
369 sizeof(CapsuleMaxStr),
370 L"Capsule%04x",
371 PcdGet16(PcdCapsuleMax)
372 );
373
374 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
375 Status = gRT->SetVariable(
376 L"CapsuleMax",
377 &gEfiCapsuleReportGuid,
378 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
379 Size,
380 CapsuleMaxStr
381 );
382 if (!EFI_ERROR(Status)) {
383 // Lock it per UEFI spec.
384 Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
385 if (!EFI_ERROR(Status)) {
386 Status = VariableLock->RequestToLock(VariableLock, L"CapsuleMax", &gEfiCapsuleReportGuid);
387 ASSERT_EFI_ERROR(Status);
388 }
389 }
390 }
391
392 /**
393 Initialize CapsuleLast variables.
394 **/
395 VOID
396 InitCapsuleLastVariable (
397 VOID
398 )
399 {
400 EFI_STATUS Status;
401 EFI_BOOT_MODE BootMode;
402 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
403 VOID *CapsuleResult;
404 UINTN Size;
405 CHAR16 CapsuleLastStr[sizeof("Capsule####")];
406
407 BootMode = GetBootModeHob();
408 if (BootMode == BOOT_ON_FLASH_UPDATE) {
409 Status = gRT->SetVariable(
410 L"CapsuleLast",
411 &gEfiCapsuleReportGuid,
412 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
413 0,
414 NULL
415 );
416 // Do not lock it because it will be updated later.
417 } else {
418 //
419 // Check if OS/APP cleared L"Capsule####"
420 //
421 ZeroMem(CapsuleLastStr, sizeof(CapsuleLastStr));
422 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
423 Status = gRT->GetVariable(
424 L"CapsuleLast",
425 &gEfiCapsuleReportGuid,
426 NULL,
427 &Size,
428 CapsuleLastStr
429 );
430 if (!EFI_ERROR(Status)) {
431 //
432 // L"CapsuleLast" is got, check if data is there.
433 //
434 Status = GetVariable2 (
435 CapsuleLastStr,
436 &gEfiCapsuleReportGuid,
437 (VOID **) &CapsuleResult,
438 NULL
439 );
440 if (EFI_ERROR(Status)) {
441 //
442 // If no data, delete L"CapsuleLast"
443 //
444 Status = gRT->SetVariable(
445 L"CapsuleLast",
446 &gEfiCapsuleReportGuid,
447 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
448 0,
449 NULL
450 );
451 }
452 }
453
454 // Lock it in normal boot path per UEFI spec.
455 Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
456 if (!EFI_ERROR(Status)) {
457 Status = VariableLock->RequestToLock(VariableLock, L"CapsuleLast", &gEfiCapsuleReportGuid);
458 ASSERT_EFI_ERROR(Status);
459 }
460 }
461 }
462
463 /**
464 Initialize capsule update variables.
465 **/
466 VOID
467 InitCapsuleUpdateVariable (
468 VOID
469 )
470 {
471 EFI_STATUS Status;
472 UINTN Index;
473 CHAR16 CapsuleVarName[30];
474 CHAR16 *TempVarName;
475
476 //
477 // Clear all the capsule variables CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
478 // as early as possible which will avoid the next time boot after the capsule update
479 // will still into the capsule loop
480 //
481 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), EFI_CAPSULE_VARIABLE_NAME);
482 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
483 Index = 0;
484 while (TRUE) {
485 if (Index > 0) {
486 UnicodeValueToString (TempVarName, 0, Index, 0);
487 }
488 Status = gRT->SetVariable (
489 CapsuleVarName,
490 &gEfiCapsuleVendorGuid,
491 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
492 0,
493 (VOID *)NULL
494 );
495 if (EFI_ERROR (Status)) {
496 //
497 // There is no capsule variables, quit
498 //
499 break;
500 }
501 Index++;
502 }
503 }
504
505 /**
506 Initialize capsule related variables.
507 **/
508 VOID
509 InitCapsuleVariable (
510 VOID
511 )
512 {
513 InitCapsuleUpdateVariable();
514 InitCapsuleMaxVariable();
515 InitCapsuleLastVariable();
516 //
517 // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast"
518 // to check status and delete them.
519 //
520 }