• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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