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