1/**@file
2
3Copyright (c) 2006, Intel Corporation. All rights reserved.<BR>
4This program and the accompanying materials
5are licensed and made available under the terms and conditions of the BSD License
6which accompanies this distribution.  The full text of the license may be found at
7http://opensource.org/licenses/bsd-license.php
8
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12Module Name:
13  FwVol.c
14
15Abstract:
16  A simple FV stack so the SEC can extract the SEC Core from an
17  FV.
18
19**/
20
21#include "SecMain.h"
22
23#define GET_OCCUPIED_SIZE(ActualSize, Alignment) \
24  (ActualSize) + (((Alignment) - ((ActualSize) & ((Alignment) - 1))) & ((Alignment) - 1))
25
26EFI_FFS_FILE_STATE
27GetFileState (
28  IN UINT8                ErasePolarity,
29  IN EFI_FFS_FILE_HEADER  *FfsHeader
30  )
31/*++
32
33Routine Description:
34  Returns the highest bit set of the State field
35
36Arguments:
37  ErasePolarity   - Erase Polarity  as defined by EFI_FVB_ERASE_POLARITY
38                    in the Attributes field.
39  FfsHeader       - Pointer to FFS File Header.
40
41Returns:
42  Returns the highest bit in the State field
43
44--*/
45{
46  EFI_FFS_FILE_STATE  FileState;
47  EFI_FFS_FILE_STATE  HighestBit;
48
49  FileState = FfsHeader->State;
50
51  if (ErasePolarity != 0) {
52    FileState = (EFI_FFS_FILE_STATE)~FileState;
53  }
54
55  HighestBit = 0x80;
56  while (HighestBit != 0 && (HighestBit & FileState) == 0) {
57    HighestBit >>= 1;
58  }
59
60  return HighestBit;
61}
62
63UINT8
64CalculateHeaderChecksum (
65  IN EFI_FFS_FILE_HEADER  *FileHeader
66  )
67/*++
68
69Routine Description:
70  Calculates the checksum of the header of a file.
71
72Arguments:
73  FileHeader       - Pointer to FFS File Header.
74
75Returns:
76  Checksum of the header.
77
78--*/
79{
80  UINT8 *ptr;
81  UINTN Index;
82  UINT8 Sum;
83
84  Sum = 0;
85  ptr = (UINT8 *) FileHeader;
86
87  for (Index = 0; Index < sizeof (EFI_FFS_FILE_HEADER) - 3; Index += 4) {
88    Sum = (UINT8) (Sum + ptr[Index]);
89    Sum = (UINT8) (Sum + ptr[Index + 1]);
90    Sum = (UINT8) (Sum + ptr[Index + 2]);
91    Sum = (UINT8) (Sum + ptr[Index + 3]);
92  }
93
94  for (; Index < sizeof (EFI_FFS_FILE_HEADER); Index++) {
95    Sum = (UINT8) (Sum + ptr[Index]);
96  }
97  //
98  // State field (since this indicates the different state of file).
99  //
100  Sum = (UINT8) (Sum - FileHeader->State);
101  //
102  // Checksum field of the file is not part of the header checksum.
103  //
104  Sum = (UINT8) (Sum - FileHeader->IntegrityCheck.Checksum.File);
105
106  return Sum;
107}
108
109EFI_STATUS
110SecFfsFindNextFile (
111  IN EFI_FV_FILETYPE             SearchType,
112  IN EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader,
113  IN OUT EFI_FFS_FILE_HEADER     **FileHeader
114  )
115/*++
116
117Routine Description:
118    Given the input file pointer, search for the next matching file in the
119    FFS volume as defined by SearchType. The search starts from FileHeader inside
120    the Firmware Volume defined by FwVolHeader.
121
122Arguments:
123    SearchType - Filter to find only files of this type.
124                 Type EFI_FV_FILETYPE_ALL causes no filtering to be done.
125    FwVolHeader - Pointer to the FV header of the volume to search.
126                  This parameter must point to a valid FFS volume.
127    FileHeader  - Pointer to the current file from which to begin searching.
128                  This pointer will be updated upon return to reflect the file
129                  found.
130
131Returns:
132    EFI_NOT_FOUND - No files matching the search criteria were found
133    EFI_SUCCESS
134
135--*/
136{
137  EFI_FFS_FILE_HEADER *FfsFileHeader;
138  UINT32              FileLength;
139  UINT32              FileOccupiedSize;
140  UINT32              FileOffset;
141  UINT64              FvLength;
142  UINT8               ErasePolarity;
143  UINT8               FileState;
144
145  FvLength = FwVolHeader->FvLength;
146  if (FwVolHeader->Attributes & EFI_FVB2_ERASE_POLARITY) {
147    ErasePolarity = 1;
148  } else {
149    ErasePolarity = 0;
150  }
151  //
152  // If FileHeader is not specified (NULL) start with the first file in the
153  // firmware volume.  Otherwise, start from the FileHeader.
154  //
155  if (*FileHeader == NULL) {
156    FfsFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FwVolHeader + FwVolHeader->HeaderLength);
157  } else {
158    //
159    // Length is 24 bits wide so mask upper 8 bits
160    // FileLength is adjusted to FileOccupiedSize as it is 8 byte aligned.
161    //
162    FileLength        = *(UINT32 *) (*FileHeader)->Size & 0x00FFFFFF;
163    FileOccupiedSize  = GET_OCCUPIED_SIZE (FileLength, 8);
164    FfsFileHeader     = (EFI_FFS_FILE_HEADER *) ((UINT8 *) *FileHeader + FileOccupiedSize);
165  }
166
167  FileOffset = (UINT32) ((UINT8 *) FfsFileHeader - (UINT8 *) FwVolHeader);
168
169  while (FileOffset < (FvLength - sizeof (EFI_FFS_FILE_HEADER))) {
170    //
171    // Get FileState which is the highest bit of the State
172    //
173    FileState = GetFileState (ErasePolarity, FfsFileHeader);
174
175    switch (FileState) {
176
177    case EFI_FILE_HEADER_INVALID:
178      FileOffset += sizeof (EFI_FFS_FILE_HEADER);
179      FfsFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsFileHeader + sizeof (EFI_FFS_FILE_HEADER));
180      break;
181
182    case EFI_FILE_DATA_VALID:
183    case EFI_FILE_MARKED_FOR_UPDATE:
184      if (CalculateHeaderChecksum (FfsFileHeader) == 0) {
185        FileLength        = *(UINT32 *) (FfsFileHeader->Size) & 0x00FFFFFF;
186        FileOccupiedSize  = GET_OCCUPIED_SIZE (FileLength, 8);
187
188        if ((SearchType == FfsFileHeader->Type) || (SearchType == EFI_FV_FILETYPE_ALL)) {
189
190          *FileHeader = FfsFileHeader;
191
192          return EFI_SUCCESS;
193        }
194
195        FileOffset += FileOccupiedSize;
196        FfsFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsFileHeader + FileOccupiedSize);
197      } else {
198        return EFI_NOT_FOUND;
199      }
200      break;
201
202    case EFI_FILE_DELETED:
203      FileLength        = *(UINT32 *) (FfsFileHeader->Size) & 0x00FFFFFF;
204      FileOccupiedSize  = GET_OCCUPIED_SIZE (FileLength, 8);
205      FileOffset += FileOccupiedSize;
206      FfsFileHeader = (EFI_FFS_FILE_HEADER *) ((UINT8 *) FfsFileHeader + FileOccupiedSize);
207      break;
208
209    default:
210      return EFI_NOT_FOUND;
211
212    }
213  }
214
215  return EFI_NOT_FOUND;
216}
217
218EFI_STATUS
219SecFfsFindSectionData (
220  IN EFI_SECTION_TYPE      SectionType,
221  IN EFI_FFS_FILE_HEADER   *FfsFileHeader,
222  IN OUT VOID              **SectionData
223  )
224/*++
225
226Routine Description:
227    Given the input file pointer, search for the next matching section in the
228    FFS volume.
229
230Arguments:
231    SearchType    - Filter to find only sections of this type.
232    FfsFileHeader - Pointer to the current file to search.
233    SectionData   - Pointer to the Section matching SectionType in FfsFileHeader.
234                     NULL if section not found
235
236Returns:
237    EFI_NOT_FOUND - No files matching the search criteria were found
238    EFI_SUCCESS
239
240--*/
241{
242  UINT32                    FileSize;
243  EFI_COMMON_SECTION_HEADER *Section;
244  UINT32                    SectionLength;
245  UINT32                    ParsedLength;
246
247  //
248  // Size is 24 bits wide so mask upper 8 bits.
249  //    Does not include FfsFileHeader header size
250  // FileSize is adjusted to FileOccupiedSize as it is 8 byte aligned.
251  //
252  Section   = (EFI_COMMON_SECTION_HEADER *) (FfsFileHeader + 1);
253  FileSize  = *(UINT32 *) (FfsFileHeader->Size) & 0x00FFFFFF;
254  FileSize -= sizeof (EFI_FFS_FILE_HEADER);
255
256  *SectionData  = NULL;
257  ParsedLength  = 0;
258  while (ParsedLength < FileSize) {
259    if (Section->Type == SectionType) {
260      *SectionData = (VOID *) (Section + 1);
261      return EFI_SUCCESS;
262    }
263    //
264    // Size is 24 bits wide so mask upper 8 bits.
265    // SectionLength is adjusted it is 4 byte aligned.
266    // Go to the next section
267    //
268    SectionLength = *(UINT32 *) Section->Size & 0x00FFFFFF;
269    SectionLength = GET_OCCUPIED_SIZE (SectionLength, 4);
270
271    ParsedLength += SectionLength;
272    Section = (EFI_COMMON_SECTION_HEADER *) ((UINT8 *) Section + SectionLength);
273  }
274
275  return EFI_NOT_FOUND;
276}
277
278EFI_STATUS
279SecFfsFindPeiCore (
280  IN  EFI_FIRMWARE_VOLUME_HEADER  *FwVolHeader,
281  OUT VOID                        **Pe32Data
282  )
283/*++
284
285Routine Description:
286  Given the pointer to the Firmware Volume Header find the SEC
287  core and return it's PE32 image.
288
289Arguments:
290  FwVolHeader - Pointer to memory mapped FV
291  Pe32Data - Pointer to SEC PE32 iamge.
292
293Returns:
294  EFI_SUCCESS - Pe32Data is valid
295  other       - Failure
296
297--*/
298{
299  EFI_STATUS          Status;
300  EFI_FFS_FILE_HEADER *FileHeader;
301  EFI_FV_FILETYPE     SearchType;
302
303  SearchType  = EFI_FV_FILETYPE_PEI_CORE;
304  FileHeader  = NULL;
305  do {
306    Status = SecFfsFindNextFile (SearchType, FwVolHeader, &FileHeader);
307    if (!EFI_ERROR (Status)) {
308      Status = SecFfsFindSectionData (EFI_SECTION_PE32, FileHeader, Pe32Data);
309      return Status;
310    }
311  } while (!EFI_ERROR (Status));
312
313  return Status;
314}
315