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