]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleReportLib.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Library / DxeCapsuleLibFmp / DxeCapsuleReportLib.c
1 /** @file
2 DXE capsule report related function.
3
4 Copyright (c) 2016 - 2017, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include <PiDxe.h>
10 #include <Protocol/FirmwareManagement.h>
11 #include <Protocol/VariableLock.h>
12 #include <Guid/CapsuleReport.h>
13 #include <Guid/FmpCapsule.h>
14 #include <Guid/CapsuleVendor.h>
15
16 #include <Library/BaseLib.h>
17 #include <Library/DebugLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/UefiBootServicesTableLib.h>
20 #include <Library/UefiRuntimeServicesTableLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiLib.h>
23 #include <Library/PcdLib.h>
24 #include <Library/HobLib.h>
25 #include <Library/PrintLib.h>
26 #include <Library/ReportStatusCodeLib.h>
27 #include <Library/DevicePathLib.h>
28 #include <Library/CapsuleLib.h>
29
30 #include <IndustryStandard/WindowsUxCapsule.h>
31
32 /**
33 Get current capsule last variable index.
34
35 @return Current capsule last variable index.
36 @retval -1 No current capsule last variable.
37 **/
38 INTN
39 GetCurrentCapsuleLastIndex (
40 VOID
41 )
42 {
43 UINTN Size;
44 CHAR16 CapsuleLastStr[sizeof("Capsule####")];
45 EFI_STATUS Status;
46 UINT16 CurrentIndex;
47
48 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
49 Status = gRT->GetVariable(
50 L"CapsuleLast",
51 &gEfiCapsuleReportGuid,
52 NULL,
53 &Size,
54 CapsuleLastStr
55 );
56 if (EFI_ERROR(Status)) {
57 return -1;
58 }
59 CurrentIndex = (UINT16)StrHexToUintn(&CapsuleLastStr[sizeof("Capsule") - 1]);
60 return CurrentIndex;
61 }
62
63 /**
64 Get a new capsule status variable index.
65
66 @return A new capsule status variable index.
67 @retval 0 No new capsule status variable index. Rolling over.
68 **/
69 INTN
70 GetNewCapsuleResultIndex (
71 VOID
72 )
73 {
74 INTN CurrentIndex;
75
76 CurrentIndex = GetCurrentCapsuleLastIndex();
77 if (CurrentIndex >= PcdGet16(PcdCapsuleMax)) {
78 DEBUG((DEBUG_INFO, " CapsuleResult variable Rolling Over!\n"));
79 return 0;
80 }
81
82 return CurrentIndex + 1;
83 }
84
85 /**
86 Write a new capsule status variable.
87
88 @param[in] CapsuleResult The capsule status variable
89 @param[in] CapsuleResultSize The size of the capsule stauts variable in bytes
90
91 @retval EFI_SUCCESS The capsule status variable is recorded.
92 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
93 **/
94 EFI_STATUS
95 WriteNewCapsuleResultVariable (
96 IN VOID *CapsuleResult,
97 IN UINTN CapsuleResultSize
98 )
99 {
100 INTN CapsuleResultIndex;
101 CHAR16 CapsuleResultStr[sizeof("Capsule####")];
102 UINTN Size;
103 EFI_STATUS Status;
104
105 CapsuleResultIndex = GetNewCapsuleResultIndex();
106 DEBUG((DEBUG_INFO, "New CapsuleResultIndex - 0x%x\n", CapsuleResultIndex));
107
108 UnicodeSPrint(
109 CapsuleResultStr,
110 sizeof(CapsuleResultStr),
111 L"Capsule%04x",
112 CapsuleResultIndex
113 );
114
115 Status = gRT->SetVariable(
116 CapsuleResultStr,
117 &gEfiCapsuleReportGuid,
118 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
119 CapsuleResultSize,
120 CapsuleResult
121 );
122 if (!EFI_ERROR(Status)) {
123 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
124 DEBUG((DEBUG_INFO, "Set CapsuleLast - %s\n", CapsuleResultStr));
125 Status = gRT->SetVariable(
126 L"CapsuleLast",
127 &gEfiCapsuleReportGuid,
128 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
129 Size,
130 CapsuleResultStr
131 );
132 }
133
134 return Status;
135 }
136
137 /**
138 Record capsule status variable and to local cache.
139
140 @param[in] CapsuleHeader The capsule image header
141 @param[in] CapsuleStatus The capsule process stauts
142
143 @retval EFI_SUCCESS The capsule status variable is recorded.
144 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
145 **/
146 EFI_STATUS
147 RecordCapsuleStatusVariable (
148 IN EFI_CAPSULE_HEADER *CapsuleHeader,
149 IN EFI_STATUS CapsuleStatus
150 )
151 {
152 EFI_CAPSULE_RESULT_VARIABLE_HEADER CapsuleResultVariable;
153 EFI_STATUS Status;
154
155 CapsuleResultVariable.VariableTotalSize = sizeof(CapsuleResultVariable);
156 CapsuleResultVariable.Reserved = 0;
157 CopyGuid (&CapsuleResultVariable.CapsuleGuid, &CapsuleHeader->CapsuleGuid);
158 ZeroMem(&CapsuleResultVariable.CapsuleProcessed, sizeof(CapsuleResultVariable.CapsuleProcessed));
159 gRT->GetTime(&CapsuleResultVariable.CapsuleProcessed, NULL);
160 CapsuleResultVariable.CapsuleStatus = CapsuleStatus;
161
162 Status = EFI_SUCCESS;
163 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
164 Status = WriteNewCapsuleResultVariable(&CapsuleResultVariable, sizeof(CapsuleResultVariable));
165 }
166 return Status;
167 }
168
169 /**
170 Record FMP capsule status variable and to local cache.
171
172 @param[in] CapsuleHeader The capsule image header
173 @param[in] CapsuleStatus The capsule process stauts
174 @param[in] PayloadIndex FMP payload index
175 @param[in] ImageHeader FMP image header
176 @param[in] FmpDevicePath DevicePath associated with the FMP producer
177
178 @retval EFI_SUCCESS The capsule status variable is recorded.
179 @retval EFI_OUT_OF_RESOURCES No resource to record the capsule status variable.
180 **/
181 EFI_STATUS
182 RecordFmpCapsuleStatusVariable (
183 IN EFI_CAPSULE_HEADER *CapsuleHeader,
184 IN EFI_STATUS CapsuleStatus,
185 IN UINTN PayloadIndex,
186 IN EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER *ImageHeader,
187 IN EFI_DEVICE_PATH_PROTOCOL *FmpDevicePath OPTIONAL
188 )
189 {
190 EFI_CAPSULE_RESULT_VARIABLE_HEADER *CapsuleResultVariableHeader;
191 EFI_CAPSULE_RESULT_VARIABLE_FMP *CapsuleResultVariableFmp;
192 EFI_STATUS Status;
193 UINT8 *CapsuleResultVariable;
194 UINTN CapsuleResultVariableSize;
195 CHAR16 *DevicePathStr;
196 UINTN DevicePathStrSize;
197
198 DevicePathStr = NULL;
199 if (FmpDevicePath != NULL) {
200 DevicePathStr = ConvertDevicePathToText (FmpDevicePath, FALSE, FALSE);
201 }
202 if (DevicePathStr != NULL) {
203 DevicePathStrSize = StrSize(DevicePathStr);
204 } else {
205 DevicePathStrSize = sizeof(CHAR16);
206 }
207 //
208 // Allocate zero CHAR16 for CapsuleFileName.
209 //
210 CapsuleResultVariableSize = sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER) + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16) + DevicePathStrSize;
211 CapsuleResultVariable = AllocateZeroPool (CapsuleResultVariableSize);
212 if (CapsuleResultVariable == NULL) {
213 return EFI_OUT_OF_RESOURCES;
214 }
215 CapsuleResultVariableHeader = (VOID *)CapsuleResultVariable;
216 CapsuleResultVariableHeader->VariableTotalSize = (UINT32)CapsuleResultVariableSize;
217 CapsuleResultVariableHeader->Reserved = 0;
218 CopyGuid(&CapsuleResultVariableHeader->CapsuleGuid, &CapsuleHeader->CapsuleGuid);
219 ZeroMem(&CapsuleResultVariableHeader->CapsuleProcessed, sizeof(CapsuleResultVariableHeader->CapsuleProcessed));
220 gRT->GetTime(&CapsuleResultVariableHeader->CapsuleProcessed, NULL);
221 CapsuleResultVariableHeader->CapsuleStatus = CapsuleStatus;
222
223 CapsuleResultVariableFmp = (VOID *)(CapsuleResultVariable + sizeof(EFI_CAPSULE_RESULT_VARIABLE_HEADER));
224 CapsuleResultVariableFmp->Version = 0x1;
225 CapsuleResultVariableFmp->PayloadIndex = (UINT8)PayloadIndex;
226 CapsuleResultVariableFmp->UpdateImageIndex = ImageHeader->UpdateImageIndex;
227 CopyGuid (&CapsuleResultVariableFmp->UpdateImageTypeId, &ImageHeader->UpdateImageTypeId);
228 if (DevicePathStr != NULL) {
229 CopyMem ((UINT8 *)CapsuleResultVariableFmp + sizeof(EFI_CAPSULE_RESULT_VARIABLE_FMP) + sizeof(CHAR16), DevicePathStr, DevicePathStrSize);
230 FreePool (DevicePathStr);
231 DevicePathStr = NULL;
232 }
233
234 Status = EFI_SUCCESS;
235 if ((CapsuleHeader->Flags & CAPSULE_FLAGS_PERSIST_ACROSS_RESET) != 0) {
236 Status = WriteNewCapsuleResultVariable(CapsuleResultVariable, CapsuleResultVariableSize);
237 }
238 FreePool (CapsuleResultVariable);
239 return Status;
240 }
241
242 /**
243 Initialize CapsuleMax variables.
244 **/
245 VOID
246 InitCapsuleMaxVariable (
247 VOID
248 )
249 {
250 EFI_STATUS Status;
251 UINTN Size;
252 CHAR16 CapsuleMaxStr[sizeof("Capsule####")];
253 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
254
255 UnicodeSPrint(
256 CapsuleMaxStr,
257 sizeof(CapsuleMaxStr),
258 L"Capsule%04x",
259 PcdGet16(PcdCapsuleMax)
260 );
261
262 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
263 Status = gRT->SetVariable(
264 L"CapsuleMax",
265 &gEfiCapsuleReportGuid,
266 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
267 Size,
268 CapsuleMaxStr
269 );
270 if (!EFI_ERROR(Status)) {
271 // Lock it per UEFI spec.
272 Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
273 if (!EFI_ERROR(Status)) {
274 Status = VariableLock->RequestToLock(VariableLock, L"CapsuleMax", &gEfiCapsuleReportGuid);
275 ASSERT_EFI_ERROR(Status);
276 }
277 }
278 }
279
280 /**
281 Initialize CapsuleLast variables.
282 **/
283 VOID
284 InitCapsuleLastVariable (
285 VOID
286 )
287 {
288 EFI_STATUS Status;
289 EFI_BOOT_MODE BootMode;
290 EDKII_VARIABLE_LOCK_PROTOCOL *VariableLock;
291 VOID *CapsuleResult;
292 UINTN Size;
293 CHAR16 CapsuleLastStr[sizeof("Capsule####")];
294
295 BootMode = GetBootModeHob();
296 if (BootMode == BOOT_ON_FLASH_UPDATE) {
297 Status = gRT->SetVariable(
298 L"CapsuleLast",
299 &gEfiCapsuleReportGuid,
300 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
301 0,
302 NULL
303 );
304 // Do not lock it because it will be updated later.
305 } else {
306 //
307 // Check if OS/APP cleared L"Capsule####"
308 //
309 ZeroMem(CapsuleLastStr, sizeof(CapsuleLastStr));
310 Size = sizeof(L"Capsule####") - sizeof(CHAR16); // no zero terminator
311 Status = gRT->GetVariable(
312 L"CapsuleLast",
313 &gEfiCapsuleReportGuid,
314 NULL,
315 &Size,
316 CapsuleLastStr
317 );
318 if (!EFI_ERROR(Status)) {
319 //
320 // L"CapsuleLast" is got, check if data is there.
321 //
322 Status = GetVariable2 (
323 CapsuleLastStr,
324 &gEfiCapsuleReportGuid,
325 (VOID **) &CapsuleResult,
326 NULL
327 );
328 if (EFI_ERROR(Status)) {
329 //
330 // If no data, delete L"CapsuleLast"
331 //
332 Status = gRT->SetVariable(
333 L"CapsuleLast",
334 &gEfiCapsuleReportGuid,
335 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
336 0,
337 NULL
338 );
339 } else {
340 if (CapsuleResult != NULL) {
341 FreePool (CapsuleResult);
342 }
343 }
344 }
345
346 // Lock it in normal boot path per UEFI spec.
347 Status = gBS->LocateProtocol(&gEdkiiVariableLockProtocolGuid, NULL, (VOID **)&VariableLock);
348 if (!EFI_ERROR(Status)) {
349 Status = VariableLock->RequestToLock(VariableLock, L"CapsuleLast", &gEfiCapsuleReportGuid);
350 ASSERT_EFI_ERROR(Status);
351 }
352 }
353 }
354
355 /**
356 Initialize capsule update variables.
357 **/
358 VOID
359 InitCapsuleUpdateVariable (
360 VOID
361 )
362 {
363 EFI_STATUS Status;
364 UINTN Index;
365 CHAR16 CapsuleVarName[30];
366 CHAR16 *TempVarName;
367
368 //
369 // Clear all the capsule variables CapsuleUpdateData, CapsuleUpdateData1, CapsuleUpdateData2...
370 // as early as possible which will avoid the next time boot after the capsule update
371 // will still into the capsule loop
372 //
373 StrCpyS (CapsuleVarName, sizeof(CapsuleVarName)/sizeof(CapsuleVarName[0]), EFI_CAPSULE_VARIABLE_NAME);
374 TempVarName = CapsuleVarName + StrLen (CapsuleVarName);
375 Index = 0;
376 while (TRUE) {
377 if (Index > 0) {
378 UnicodeValueToStringS (
379 TempVarName,
380 sizeof (CapsuleVarName) - ((UINTN)TempVarName - (UINTN)CapsuleVarName),
381 0,
382 Index,
383 0
384 );
385 }
386 Status = gRT->SetVariable (
387 CapsuleVarName,
388 &gEfiCapsuleVendorGuid,
389 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_BOOTSERVICE_ACCESS,
390 0,
391 (VOID *)NULL
392 );
393 if (EFI_ERROR (Status)) {
394 //
395 // There is no capsule variables, quit
396 //
397 break;
398 }
399 Index++;
400 }
401 }
402
403 /**
404 Initialize capsule related variables.
405 **/
406 VOID
407 InitCapsuleVariable (
408 VOID
409 )
410 {
411 InitCapsuleUpdateVariable();
412 InitCapsuleMaxVariable();
413 InitCapsuleLastVariable();
414 //
415 // No need to clear L"Capsule####", because OS/APP should refer L"CapsuleLast"
416 // to check status and delete them.
417 //
418 }