1 /** @file
2   Esrt management implementation.
3 
4 Copyright (c) 2015 - 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 "EsrtImpl.h"
16 
17 /**
18   Find Esrt Entry stored in ESRT repository.
19 
20   @param[in]     FwClass           Firmware class guid in Esrt entry
21   @param[in]     Attribute         Esrt from Non FMP or FMP instance
22   @param[out]    Entry             Esrt entry returned
23 
24   @retval EFI_SUCCESS            Successfully find an Esrt entry
25   @retval EF_NOT_FOUND           No Esrt entry found
26 
27 **/
28 EFI_STATUS
GetEsrtEntry(IN EFI_GUID * FwClass,IN UINTN Attribute,OUT EFI_SYSTEM_RESOURCE_ENTRY * Entry)29 GetEsrtEntry (
30   IN  EFI_GUID              *FwClass,
31   IN  UINTN                 Attribute,
32   OUT EFI_SYSTEM_RESOURCE_ENTRY *Entry
33   )
34 {
35   EFI_STATUS                 Status;
36   CHAR16                     *VariableName;
37   EFI_SYSTEM_RESOURCE_ENTRY  *EsrtRepository;
38   UINTN                      RepositorySize;
39   UINTN                      Index;
40   UINTN                      EsrtNum;
41 
42   EsrtRepository = NULL;
43 
44   //
45   // Get Esrt index buffer
46   //
47   if (Attribute == ESRT_FROM_FMP) {
48     VariableName = EFI_ESRT_FMP_VARIABLE_NAME;
49   } else {
50     VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME;
51   }
52 
53   Status = GetVariable2 (
54              VariableName,
55              &gEfiCallerIdGuid,
56              (VOID **) &EsrtRepository,
57              &RepositorySize
58              );
59 
60   if (EFI_ERROR(Status)) {
61     goto EXIT;
62   }
63 
64   if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) {
65     DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n"));
66     Status = EFI_ABORTED;
67     goto EXIT;
68   }
69 
70   Status  = EFI_NOT_FOUND;
71   EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY);
72   for (Index = 0; Index < EsrtNum; Index++) {
73     if (CompareGuid(FwClass, &EsrtRepository[Index].FwClass)) {
74       CopyMem(Entry, &EsrtRepository[Index], sizeof(EFI_SYSTEM_RESOURCE_ENTRY));
75       Status = EFI_SUCCESS;
76       break;
77     }
78   }
79 
80 EXIT:
81   if (EsrtRepository != NULL) {
82     FreePool(EsrtRepository);
83   }
84 
85   return Status;
86 }
87 
88 /**
89   Insert a new ESRT entry into ESRT Cache repository.
90 
91   @param[in]  Entry                Esrt entry to be set
92   @param[in]  Attribute            Esrt from Esrt private protocol or FMP instance
93 
94   @retval EFI_SUCCESS          Successfully set a variable.
95 
96 **/
97 EFI_STATUS
InsertEsrtEntry(IN EFI_SYSTEM_RESOURCE_ENTRY * Entry,UINTN Attribute)98 InsertEsrtEntry(
99   IN EFI_SYSTEM_RESOURCE_ENTRY *Entry,
100   UINTN                        Attribute
101   )
102 {
103   EFI_STATUS                 Status;
104   CHAR16                     *VariableName;
105   EFI_SYSTEM_RESOURCE_ENTRY  *EsrtRepository;
106   UINTN                      RepositorySize;
107   EFI_SYSTEM_RESOURCE_ENTRY  *EsrtRepositoryNew;
108 
109   EsrtRepository    = NULL;
110   EsrtRepositoryNew = NULL;
111 
112   //
113   // Get Esrt index buffer
114   //
115   if (Attribute == ESRT_FROM_FMP) {
116     VariableName = EFI_ESRT_FMP_VARIABLE_NAME;
117   } else {
118     VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME;
119   }
120 
121   Status = GetVariable2 (
122              VariableName,
123              &gEfiCallerIdGuid,
124              (VOID **) &EsrtRepository,
125              &RepositorySize
126              );
127 
128   if (Status == EFI_NOT_FOUND) {
129     //
130     // If not exist, create new Esrt cache repository
131     //
132     Status = gRT->SetVariable(
133                     VariableName,
134                     &gEfiCallerIdGuid,
135                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
136                     sizeof(EFI_SYSTEM_RESOURCE_ENTRY),
137                     Entry
138                     );
139     return Status;
140 
141   } else if (Status == EFI_SUCCESS) {
142     //
143     // if exist, update Esrt cache repository
144     //
145     if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) {
146       DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n"));
147       //
148       // Repository is corrupt. Clear Repository before insert new entry
149       //
150       Status = gRT->SetVariable(
151                       VariableName,
152                       &gEfiCallerIdGuid,
153                       EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
154                       0,
155                       EsrtRepository
156                       );
157       FreePool(EsrtRepository);
158       RepositorySize = 0;
159       EsrtRepository = NULL;
160     }
161 
162     //
163     // Check Repository size constraint
164     //
165     if ((Attribute == ESRT_FROM_FMP && RepositorySize >= PcdGet32(PcdMaxFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY))
166       ||(Attribute == ESRT_FROM_NONFMP && RepositorySize >= PcdGet32(PcdMaxNonFmpEsrtCacheNum) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY)) ) {
167       Status = EFI_OUT_OF_RESOURCES;
168       goto EXIT;
169     }
170 
171     EsrtRepositoryNew = AllocatePool(RepositorySize + sizeof(EFI_SYSTEM_RESOURCE_ENTRY));
172     if (EsrtRepositoryNew == NULL) {
173       Status = EFI_OUT_OF_RESOURCES;
174       goto EXIT;
175     }
176 
177     if (RepositorySize != 0 && EsrtRepository != NULL) {
178       CopyMem(EsrtRepositoryNew, EsrtRepository, RepositorySize);
179     }
180     CopyMem((UINT8 *)EsrtRepositoryNew + RepositorySize, Entry, sizeof(EFI_SYSTEM_RESOURCE_ENTRY));
181 
182     Status = gRT->SetVariable(
183                     VariableName,
184                     &gEfiCallerIdGuid,
185                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
186                     RepositorySize + sizeof(EFI_SYSTEM_RESOURCE_ENTRY),
187                     EsrtRepositoryNew
188                     );
189   }
190 
191 EXIT:
192   if (EsrtRepository != NULL) {
193     FreePool(EsrtRepository);
194   }
195 
196   if (EsrtRepositoryNew != NULL) {
197     FreePool(EsrtRepositoryNew);
198   }
199 
200   return Status;
201 }
202 
203 /**
204   Delete ESRT Entry from ESRT repository.
205 
206   @param[in]    FwClass              FwClass of Esrt entry to delete
207   @param[in]    Attribute            Esrt from Esrt private protocol or FMP instance
208 
209   @retval EFI_SUCCESS         Insert all entries Successfully
210   @retval EFI_NOT_FOUND       ESRT entry with FwClass doesn't exsit
211 
212 **/
213 EFI_STATUS
DeleteEsrtEntry(IN EFI_GUID * FwClass,IN UINTN Attribute)214 DeleteEsrtEntry(
215   IN  EFI_GUID        *FwClass,
216   IN  UINTN           Attribute
217   )
218 {
219   EFI_STATUS                 Status;
220   CHAR16                     *VariableName;
221   EFI_SYSTEM_RESOURCE_ENTRY  *EsrtRepository;
222   UINTN                      RepositorySize;
223   UINTN                      Index;
224   UINTN                      EsrtNum;
225 
226   EsrtRepository = NULL;
227 
228   //
229   // Get Esrt index buffer
230   //
231   if (Attribute == ESRT_FROM_FMP) {
232     VariableName = EFI_ESRT_FMP_VARIABLE_NAME;
233   } else {
234     VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME;
235   }
236 
237   Status = GetVariable2 (
238              VariableName,
239              &gEfiCallerIdGuid,
240              (VOID **) &EsrtRepository,
241              &RepositorySize
242              );
243 
244   if (EFI_ERROR(Status)) {
245     goto EXIT;
246   }
247 
248   if ((RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY)) != 0) {
249     DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n"));
250     //
251     // Repository is corrupt. Clear Repository before insert new entry
252     //
253     Status = gRT->SetVariable(
254                     VariableName,
255                     &gEfiCallerIdGuid,
256                     EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
257                     0,
258                     EsrtRepository
259                     );
260     goto EXIT;
261   }
262 
263   Status = EFI_NOT_FOUND;
264   EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY);
265   for (Index = 0; Index < EsrtNum; Index++) {
266     //
267     // Delete Esrt entry if it is found in repository
268     //
269     if (CompareGuid(FwClass, &EsrtRepository[Index].FwClass)) {
270       //
271       // If delete Esrt entry is not at the rail
272       //
273       if (Index < EsrtNum - 1) {
274         CopyMem(&EsrtRepository[Index], &EsrtRepository[Index + 1], (EsrtNum - Index - 1) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY));
275       }
276 
277       //
278       // Update New Repository
279       //
280       Status = gRT->SetVariable(
281                       VariableName,
282                       &gEfiCallerIdGuid,
283                       EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
284                       (EsrtNum - 1) * sizeof(EFI_SYSTEM_RESOURCE_ENTRY),
285                       EsrtRepository
286                       );
287       break;
288     }
289   }
290 
291 EXIT:
292   if (EsrtRepository != NULL) {
293     FreePool(EsrtRepository);
294   }
295 
296   return Status;
297 
298 }
299 
300 /**
301   Update one ESRT entry in ESRT repository
302 
303   @param[in]    Entry                Esrt entry to be set
304   @param[in]    Attribute            Esrt from Non Esrt or FMP instance
305 
306   @retval EFI_SUCCESS          Successfully Update a variable.
307   @retval EFI_NOT_FOUND        The Esrt enry doesn't exist
308 
309 **/
310 EFI_STATUS
UpdateEsrtEntry(IN EFI_SYSTEM_RESOURCE_ENTRY * Entry,UINTN Attribute)311 UpdateEsrtEntry(
312   IN EFI_SYSTEM_RESOURCE_ENTRY *Entry,
313   UINTN                        Attribute
314   )
315 {
316   EFI_STATUS                 Status;
317   CHAR16                     *VariableName;
318   EFI_SYSTEM_RESOURCE_ENTRY  *EsrtRepository;
319   UINTN                      RepositorySize;
320   UINTN                      Index;
321   UINTN                      EsrtNum;
322 
323   EsrtRepository    = NULL;
324 
325   //
326   // Get Esrt index buffer
327   //
328   if (Attribute == ESRT_FROM_FMP) {
329     VariableName = EFI_ESRT_FMP_VARIABLE_NAME;
330   } else {
331     VariableName = EFI_ESRT_NONFMP_VARIABLE_NAME;
332   }
333 
334   Status = GetVariable2 (
335              VariableName,
336              &gEfiCallerIdGuid,
337              (VOID **) &EsrtRepository,
338              &RepositorySize
339              );
340 
341   if (!EFI_ERROR(Status)) {
342     //
343     // if exist, update Esrt cache repository
344     //
345     if (RepositorySize % sizeof(EFI_SYSTEM_RESOURCE_ENTRY) != 0) {
346       DEBUG((EFI_D_ERROR, "Repository Corrupt. Need to rebuild Repository.\n"));
347       //
348       // Repository is corrupt. Clear Repository before insert new entry
349       //
350       Status = gRT->SetVariable(
351                       VariableName,
352                       &gEfiCallerIdGuid,
353                       EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
354                       0,
355                       EsrtRepository
356                       );
357       Status = EFI_NOT_FOUND;
358       goto EXIT;
359     }
360 
361     Status = EFI_NOT_FOUND;
362     EsrtNum = RepositorySize/sizeof(EFI_SYSTEM_RESOURCE_ENTRY);
363     for (Index = 0; Index < EsrtNum; Index++) {
364       //
365       // Update Esrt entry if it is found in repository
366       //
367       if (CompareGuid(&Entry->FwClass, &EsrtRepository[Index].FwClass)) {
368 
369         CopyMem(&EsrtRepository[Index], Entry, sizeof(EFI_SYSTEM_RESOURCE_ENTRY));
370         //
371         // Update New Repository
372         //
373         Status = gRT->SetVariable(
374                         VariableName,
375                         &gEfiCallerIdGuid,
376                         EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
377                         RepositorySize,
378                         EsrtRepository
379                         );
380         break;
381       }
382     }
383   }
384 
385 EXIT:
386   if (EsrtRepository != NULL) {
387     FreePool(EsrtRepository);
388   }
389 
390   return Status;
391 }
392 
393 /**
394   Return if this FMP is a system FMP or a device FMP, based upon FmpImageInfo.
395 
396   @param[in] FmpImageInfo A pointer to EFI_FIRMWARE_IMAGE_DESCRIPTOR
397 
398   @return TRUE  It is a system FMP.
399   @return FALSE It is a device FMP.
400 **/
401 BOOLEAN
IsSystemFmp(IN EFI_FIRMWARE_IMAGE_DESCRIPTOR * FmpImageInfo)402 IsSystemFmp (
403   IN EFI_FIRMWARE_IMAGE_DESCRIPTOR   *FmpImageInfo
404   )
405 {
406   GUID      *Guid;
407   UINTN     Count;
408   UINTN     Index;
409 
410   Guid = PcdGetPtr(PcdSystemFmpCapsuleImageTypeIdGuid);
411   Count = PcdGetSize(PcdSystemFmpCapsuleImageTypeIdGuid)/sizeof(GUID);
412 
413   for (Index = 0; Index < Count; Index++, Guid++) {
414     if (CompareGuid(&FmpImageInfo->ImageTypeId, Guid)) {
415       return TRUE;
416     }
417   }
418 
419   return FALSE;
420 }
421 
422 /**
423   Init one ESRT entry according to input FmpImageInfo (V1, V2, V3) .
424 
425   @param[in, out]     EsrtEntry            Esrt entry to be Init
426   @param[in]          FmpImageInfo         FMP image info descriptor
427   @param[in]          DescriptorVersion    FMP Image info descriptor version
428 
429 **/
430 VOID
SetEsrtEntryFromFmpInfo(IN OUT EFI_SYSTEM_RESOURCE_ENTRY * EsrtEntry,IN EFI_FIRMWARE_IMAGE_DESCRIPTOR * FmpImageInfo,IN UINT32 DescriptorVersion)431 SetEsrtEntryFromFmpInfo (
432   IN OUT EFI_SYSTEM_RESOURCE_ENTRY   *EsrtEntry,
433   IN EFI_FIRMWARE_IMAGE_DESCRIPTOR   *FmpImageInfo,
434   IN UINT32                          DescriptorVersion
435   )
436 {
437   EsrtEntry->FwVersion                = FmpImageInfo->Version;
438   EsrtEntry->FwClass                  = FmpImageInfo->ImageTypeId;
439   if (IsSystemFmp(FmpImageInfo)) {
440     EsrtEntry->FwType                   = ESRT_FW_TYPE_SYSTEMFIRMWARE;
441   } else {
442     EsrtEntry->FwType                   = ESRT_FW_TYPE_DEVICEFIRMWARE;
443   }
444   EsrtEntry->LowestSupportedFwVersion = 0;
445   EsrtEntry->CapsuleFlags             = 0;
446   EsrtEntry->LastAttemptVersion       = 0;
447   EsrtEntry->LastAttemptStatus        = LAST_ATTEMPT_STATUS_SUCCESS;
448 
449   if (DescriptorVersion >= 2) {
450     //
451     // LowestSupportedImageVersion only available in FMP V2 or higher
452     //
453     EsrtEntry->LowestSupportedFwVersion = FmpImageInfo->LowestSupportedImageVersion;
454   }
455 
456   if (DescriptorVersion >= 3) {
457     //
458     // LastAttemptVersion & LastAttemptStatus only available in FMP V3 or higher
459     //
460     EsrtEntry->LastAttemptVersion = FmpImageInfo->LastAttemptVersion;
461     EsrtEntry->LastAttemptStatus  = FmpImageInfo->LastAttemptStatus;
462   }
463 
464   //
465   // Set capsule customized flag
466   //
467   if ((FmpImageInfo->AttributesSupported & IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0
468    && (FmpImageInfo->AttributesSetting & IMAGE_ATTRIBUTE_RESET_REQUIRED) != 0) {
469     EsrtEntry->CapsuleFlags = PcdGet16(PcdSystemRebootAfterCapsuleProcessFlag);
470   }
471 }
472