1 /** @file
2 
3 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
4 Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.<BR>
5 
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7 
8 **/
9 
10 #include <Library/FvLib.h>
11 
12 #include <Library/BaseLib.h>
13 #include <Library/BaseMemoryLib.h>
14 #include <Library/DebugLib.h>
15 
16 #define GET_OCCUPIED_SIZE(ActualSize, Alignment) \
17   (ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1))
18 
19 /**
20   Returns the highest bit set of the State field
21 
22   @param ErasePolarity   Erase Polarity  as defined by EFI_FVB_ERASE_POLARITY
23                          in the Attributes field.
24   @param FfsHeader       Pointer to FFS File Header.
25 
26   @return the highest bit in the State field
27 **/
28 EFI_FFS_FILE_STATE
GetFileState(IN UINT8 ErasePolarity,IN EFI_FFS_FILE_HEADER * FfsHeader)29 GetFileState (
30   IN UINT8                ErasePolarity,
31   IN EFI_FFS_FILE_HEADER  *FfsHeader
32   )
33 {
34   EFI_FFS_FILE_STATE  FileState;
35   EFI_FFS_FILE_STATE  HighestBit;
36 
37   FileState = FfsHeader->State;
38 
39   if (ErasePolarity != 0) {
40     FileState = (EFI_FFS_FILE_STATE)~FileState;
41   }
42 
43   HighestBit = 0x80;
44   while (HighestBit != 0 && (HighestBit & FileState) == 0) {
45     HighestBit >>= 1;
46   }
47 
48   return HighestBit;
49 }
50 
51 /**
52   Calculates the checksum of the header of a file.
53 
54   @param FileHeader       Pointer to FFS File Header.
55 
56   @return Checksum of the header.
57 **/
58 UINT8
CalculateHeaderChecksum(IN EFI_FFS_FILE_HEADER * FileHeader)59 CalculateHeaderChecksum (
60   IN EFI_FFS_FILE_HEADER  *FileHeader
61   )
62 {
63   UINT8 *ptr;
64   UINTN Index;
65   UINT8 Sum;
66 
67   Sum = 0;
68   ptr = (UINT8 *) FileHeader;
69 
70   for (Index = 0; Index < sizeof (EFI_FFS_FILE_HEADER) - 3; Index += 4) {
71     Sum = (UINT8) (Sum + ptr[Index]);
72     Sum = (UINT8) (Sum + ptr[Index + 1]);
73     Sum = (UINT8) (Sum + ptr[Index + 2]);
74     Sum = (UINT8) (Sum + ptr[Index + 3]);
75   }
76 
77   for (; Index < sizeof (EFI_FFS_FILE_HEADER); Index++) {
78     Sum = (UINT8) (Sum + ptr[Index]);
79   }
80   //
81   // State field (since this indicates the different state of file).
82   //
83   Sum = (UINT8) (Sum - FileHeader->State);
84   //
85   // Checksum field of the file is not part of the header checksum.
86   //
87   Sum = (UINT8) (Sum - FileHeader->IntegrityCheck.Checksum.File);
88 
89   return Sum;
90 }
91 
92 /**
93   Given the input file pointer, search for the next matching file in the
94   FFS volume as defined by SearchType. The search starts from FileHeader inside
95   the Firmware Volume defined by FwVolHeader.
96 
97   @param  SearchType  Filter to find only files of this type.
98                       Type EFI_FV_FILETYPE_ALL causes no filtering to be done.
99   @param  FwVolHeader Pointer to the FV header of the volume to search.
100                       This parameter must point to a valid FFS volume.
101   @param  FileHeader  Pointer to the current file from which to begin searching.
102                       This pointer will be updated upon return to reflect the file found.
103 
104   @retval EFI_NOT_FOUND  No files matching the search criteria were found
105   @retval EFI_SUCCESS
106 **/
107 EFI_STATUS
108 EFIAPI
FfsFindNextFile(IN EFI_FV_FILETYPE SearchType,IN EFI_FIRMWARE_VOLUME_HEADER * FwVolHeader,IN OUT EFI_FFS_FILE_HEADER ** FileHeader)109 FfsFindNextFile (
110   IN EFI_FV_FILETYPE             SearchType,
111   IN EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader,
112   IN OUT EFI_FFS_FILE_HEADER     **FileHeader
113   )
114 {
115   EFI_FIRMWARE_VOLUME_EXT_HEADER *FvExtHeader;
116 
117   EFI_FFS_FILE_HEADER *FfsFileHeader;
118   UINT32              FileLength;
119   UINT32              FileOccupiedSize;
120   UINT32              FileOffset;
121   UINT64              FvLength;
122   UINT8               ErasePolarity;
123   UINT8               FileState;
124 
125   FvLength = FwVolHeader->FvLength;
126   if (FwVolHeader->Attributes & EFI_FVB2_ERASE_POLARITY) {
127     ErasePolarity = 1;
128   } else {
129     ErasePolarity = 0;
130   }
131   //
132   // If FileHeader is not specified (NULL) start with the first file in the
133   // firmware volume.  Otherwise, start from the FileHeader.
134   //
135   if (*FileHeader == NULL) {
136 
137     if (FwVolHeader->ExtHeaderOffset != 0) {
138 
139       FvExtHeader = (EFI_FIRMWARE_VOLUME_EXT_HEADER *)((UINT8 *)FwVolHeader +
140                                                        FwVolHeader->ExtHeaderOffset);
141 
142       FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FvExtHeader +
143                                               FvExtHeader->ExtHeaderSize);
144 
145     } else {
146 
147       FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINT8 *)FwVolHeader +
148                                               FwVolHeader->HeaderLength);
149 
150     }
151 
152     FfsFileHeader = (EFI_FFS_FILE_HEADER *)((UINTN)FwVolHeader +
153                                             ALIGN_VALUE((UINTN)FfsFileHeader -
154                                                         (UINTN)FwVolHeader, 8));
155   } else {
156     //
157     // Length is 24 bits wide so mask upper 8 bits
158     // FileLength is adjusted to FileOccupiedSize as it is 8 byte aligned.
159     //
160     FileLength        = FFS_FILE_SIZE(*FileHeader);
161     FileOccupiedSize  = GET_OCCUPIED_SIZE (FileLength, 8);
162     FfsFileHeader     = (EFI_FFS_FILE_HEADER *) ((UINT8 *) *FileHeader + FileOccupiedSize);
163   }
164 
165   FileOffset = (UINT32) ((UINT8 *) FfsFileHeader - (UINT8 *) FwVolHeader);
166 
167   while (FileOffset < (FvLength - sizeof (EFI_FFS_FILE_HEADER))) {
168     //
169     // Get FileState which is the highest bit of the State
170     //
171     FileState = GetFileState (ErasePolarity, FfsFileHeader);
172 
173     switch (FileState) {
174 
175     case EFI_FILE_HEADER_INVALID:
176       FileOffset += sizeof (EFI_FFS_FILE_HEADER);
177       FfsFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsFileHeader + sizeof (EFI_FFS_FILE_HEADER));
178       break;
179 
180     case EFI_FILE_DATA_VALID:
181     case EFI_FILE_MARKED_FOR_UPDATE:
182       if (CalculateHeaderChecksum (FfsFileHeader) == 0) {
183         FileLength        = FFS_FILE_SIZE(FfsFileHeader);
184         FileOccupiedSize  = GET_OCCUPIED_SIZE (FileLength, 8);
185 
186         if ((SearchType == FfsFileHeader->Type) || (SearchType == EFI_FV_FILETYPE_ALL)) {
187 
188           *FileHeader = FfsFileHeader;
189 
190           return EFI_SUCCESS;
191         }
192 
193         FileOffset += FileOccupiedSize;
194         FfsFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsFileHeader + FileOccupiedSize);
195       } else {
196         return EFI_NOT_FOUND;
197       }
198       break;
199 
200     case EFI_FILE_DELETED:
201       FileLength        = FFS_FILE_SIZE(FfsFileHeader);
202       FileOccupiedSize  = GET_OCCUPIED_SIZE (FileLength, 8);
203       FileOffset += FileOccupiedSize;
204       FfsFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsFileHeader + FileOccupiedSize);
205       break;
206 
207     default:
208       return EFI_NOT_FOUND;
209 
210     }
211   }
212 
213   return EFI_NOT_FOUND;
214 }
215 
216 /**
217   Locates a section within a series of sections
218   with the specified section type.
219 
220   @param[in]   Sections        The sections to search
221   @param[in]   SizeOfSections  Total size of all sections
222   @param[in]   SectionType     The section type to locate
223   @param[out]  FoundSection    The FFS section if found
224 
225   @retval EFI_SUCCESS           The file and section was found
226   @retval EFI_NOT_FOUND         The file and section was not found
227   @retval EFI_VOLUME_CORRUPTED  The firmware volume was corrupted
228 **/
229 EFI_STATUS
230 EFIAPI
FindFfsSectionInSections(IN VOID * Sections,IN UINTN SizeOfSections,IN EFI_SECTION_TYPE SectionType,OUT EFI_COMMON_SECTION_HEADER ** FoundSection)231 FindFfsSectionInSections (
232   IN  VOID                             *Sections,
233   IN  UINTN                            SizeOfSections,
234   IN  EFI_SECTION_TYPE                 SectionType,
235   OUT EFI_COMMON_SECTION_HEADER        **FoundSection
236   )
237 {
238   EFI_PHYSICAL_ADDRESS        CurrentAddress;
239   UINT32                      Size;
240   EFI_PHYSICAL_ADDRESS        EndOfSections;
241   EFI_COMMON_SECTION_HEADER   *Section;
242   EFI_PHYSICAL_ADDRESS        EndOfSection;
243 
244   //
245   // Loop through the FFS file sections
246   //
247   EndOfSection = (EFI_PHYSICAL_ADDRESS)(UINTN) Sections;
248   EndOfSections = EndOfSection + SizeOfSections;
249   for (;;) {
250     if (EndOfSection == EndOfSections) {
251       break;
252     }
253     CurrentAddress = EndOfSection;
254 
255     Section = (EFI_COMMON_SECTION_HEADER*)(UINTN) CurrentAddress;
256 
257     Size = SECTION_SIZE (Section);
258     if (Size < sizeof (*Section)) {
259       return EFI_VOLUME_CORRUPTED;
260     }
261 
262     EndOfSection = CurrentAddress + Size;
263     if (EndOfSection > EndOfSections) {
264       return EFI_VOLUME_CORRUPTED;
265     }
266     Size = GET_OCCUPIED_SIZE (Size, 4);
267 
268     //
269     // Look for the requested section type
270     //
271     if (Section->Type == SectionType) {
272       *FoundSection = Section;
273       return EFI_SUCCESS;
274     }
275   }
276 
277   return EFI_NOT_FOUND;
278 }
279 
280 /**
281   Given the input file pointer, search for the next matching section in the
282   FFS volume.
283 
284   @param  SearchType    Filter to find only sections of this type.
285   @param  FfsFileHeader Pointer to the current file to search.
286   @param  SectionHeader Pointer to the Section matching SectionType in FfsFileHeader.
287                         NULL if section not found
288 
289   @retval  EFI_NOT_FOUND  No files matching the search criteria were found
290   @retval  EFI_SUCCESS
291 **/
292 EFI_STATUS
293 EFIAPI
FfsFindSection(IN EFI_SECTION_TYPE SectionType,IN EFI_FFS_FILE_HEADER * FfsFileHeader,IN OUT EFI_COMMON_SECTION_HEADER ** SectionHeader)294 FfsFindSection (
295   IN EFI_SECTION_TYPE              SectionType,
296   IN EFI_FFS_FILE_HEADER           *FfsFileHeader,
297   IN OUT EFI_COMMON_SECTION_HEADER **SectionHeader
298   )
299 {
300   UINT32                    FileSize;
301   EFI_COMMON_SECTION_HEADER *Section;
302   EFI_STATUS                Status;
303 
304   //
305   // Size is 24 bits wide so mask upper 8 bits.
306   //    Does not include FfsFileHeader header size
307   // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned.
308   //
309   Section   = (EFI_COMMON_SECTION_HEADER *) (FfsFileHeader + 1);
310   FileSize  = FFS_FILE_SIZE(FfsFileHeader);
311   FileSize -= sizeof (EFI_FFS_FILE_HEADER);
312 
313   Status = FindFfsSectionInSections (
314              Section,
315              FileSize,
316              SectionType,
317              SectionHeader
318              );
319   return Status;
320 }
321 
322 /**
323   Given the input file pointer, search for the next matching section in the
324   FFS volume.
325 
326   @param  SearchType      Filter to find only sections of this type.
327   @param  FfsFileHeader   Pointer to the current file to search.
328   @param  SectionData     Pointer to the Section matching SectionType in FfsFileHeader.
329                           NULL if section not found
330   @param  SectionDataSize The size of SectionData
331 
332   @retval  EFI_NOT_FOUND  No files matching the search criteria were found
333   @retval  EFI_SUCCESS
334 **/
335 EFI_STATUS
336 EFIAPI
FfsFindSectionData(IN EFI_SECTION_TYPE SectionType,IN EFI_FFS_FILE_HEADER * FfsFileHeader,IN OUT VOID ** SectionData,IN OUT UINTN * SectionDataSize)337 FfsFindSectionData (
338   IN EFI_SECTION_TYPE      SectionType,
339   IN EFI_FFS_FILE_HEADER   *FfsFileHeader,
340   IN OUT VOID              **SectionData,
341   IN OUT UINTN             *SectionDataSize
342   )
343 {
344   UINT32                    FileSize;
345   EFI_COMMON_SECTION_HEADER *Section;
346   UINT32                    SectionLength;
347   UINT32                    ParsedLength;
348 
349   //
350   // Size is 24 bits wide so mask upper 8 bits.
351   // Does not include FfsFileHeader header size
352   // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned.
353   //
354   Section   = (EFI_COMMON_SECTION_HEADER *) (FfsFileHeader + 1);
355   FileSize  = FFS_FILE_SIZE(FfsFileHeader);
356   FileSize -= sizeof (EFI_FFS_FILE_HEADER);
357 
358   *SectionData  = NULL;
359   ParsedLength  = 0;
360   while (ParsedLength < FileSize) {
361     if (Section->Type == SectionType) {
362       *SectionData = (VOID *) (Section + 1);
363       *SectionDataSize = SECTION_SIZE(Section);
364       return EFI_SUCCESS;
365     }
366     //
367     // Size is 24 bits wide so mask upper 8 bits.
368     // SectionLength is adjusted it is 4 byte aligned.
369     // Go to the next section
370     //
371     SectionLength = SECTION_SIZE(Section);
372     SectionLength = GET_OCCUPIED_SIZE (SectionLength, 4);
373 
374     ParsedLength += SectionLength;
375     Section = (EFI_COMMON_SECTION_HEADER *) ((UINT8 *) Section + SectionLength);
376   }
377 
378   return EFI_NOT_FOUND;
379 }
380