1 /** @file
2 This file contains functions required to generate a Firmware File System file.
3 
4 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 
9 #ifndef __GNUC__
10 #include <windows.h>
11 #include <io.h>
12 #include <sys/types.h>
13 #include <sys/stat.h>
14 #endif
15 
16 #ifdef __GNUC__
17 #include <unistd.h>
18 #endif
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 
24 #include <Common/UefiBaseTypes.h>
25 #include <Common/PiFirmwareFile.h>
26 #include <IndustryStandard/PeImage.h>
27 #include <Guid/FfsSectionAlignmentPadding.h>
28 
29 #include "CommonLib.h"
30 #include "ParseInf.h"
31 #include "EfiUtilityMsgs.h"
32 #include "FvLib.h"
33 #include "PeCoffLib.h"
34 
35 #define UTILITY_NAME            "GenFfs"
36 #define UTILITY_MAJOR_VERSION   0
37 #define UTILITY_MINOR_VERSION   1
38 
39 STATIC CHAR8 *mFfsFileType[] = {
40   NULL,                                   // 0x00
41   "EFI_FV_FILETYPE_RAW",                  // 0x01
42   "EFI_FV_FILETYPE_FREEFORM",             // 0x02
43   "EFI_FV_FILETYPE_SECURITY_CORE",        // 0x03
44   "EFI_FV_FILETYPE_PEI_CORE",             // 0x04
45   "EFI_FV_FILETYPE_DXE_CORE",             // 0x05
46   "EFI_FV_FILETYPE_PEIM",                 // 0x06
47   "EFI_FV_FILETYPE_DRIVER",               // 0x07
48   "EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER", // 0x08
49   "EFI_FV_FILETYPE_APPLICATION",          // 0x09
50   "EFI_FV_FILETYPE_SMM",                  // 0x0A
51   "EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE",// 0x0B
52   "EFI_FV_FILETYPE_COMBINED_SMM_DXE",     // 0x0C
53   "EFI_FV_FILETYPE_SMM_CORE",             // 0x0D
54   "EFI_FV_FILETYPE_MM_STANDALONE",        // 0x0E
55   "EFI_FV_FILETYPE_MM_CORE_STANDALONE"    // 0x0F
56 };
57 
58 STATIC CHAR8 *mAlignName[] = {
59   "1", "2", "4", "8", "16", "32", "64", "128", "256", "512",
60   "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K",
61   "512K", "1M", "2M", "4M", "8M", "16M"
62  };
63 
64 STATIC CHAR8 *mFfsValidAlignName[] = {
65   "8", "16", "128", "512", "1K", "4K", "32K", "64K", "128K","256K",
66   "512K", "1M", "2M", "4M", "8M", "16M"
67  };
68 
69 STATIC UINT32 mFfsValidAlign[] = {0, 8, 16, 128, 512, 1024, 4096, 32768, 65536, 131072, 262144,
70                                   524288, 1048576, 2097152, 4194304, 8388608, 16777216};
71 
72 STATIC EFI_GUID mZeroGuid = {0};
73 
74 STATIC EFI_GUID mEfiFfsSectionAlignmentPaddingGuid = EFI_FFS_SECTION_ALIGNMENT_PADDING_GUID;
75 
76 STATIC
77 VOID
Version(VOID)78 Version (
79   VOID
80   )
81 /*++
82 
83 Routine Description:
84 
85   Print out version information for this utility.
86 
87 Arguments:
88 
89   None
90 
91 Returns:
92 
93   None
94 
95 --*/
96 {
97   fprintf (stdout, "%s Version %d.%d %s \n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION, __BUILD_VERSION);
98 }
99 
100 STATIC
101 VOID
Usage(VOID)102 Usage (
103   VOID
104   )
105 /*++
106 
107 Routine Description:
108 
109   Print Error / Help message.
110 
111 Arguments:
112 
113   VOID
114 
115 Returns:
116 
117   None
118 
119 --*/
120 {
121   //
122   // Summary usage
123   //
124   fprintf (stdout, "\nUsage: %s [options]\n\n", UTILITY_NAME);
125 
126   //
127   // Copyright declaration
128   //
129   fprintf (stdout, "Copyright (c) 2007 - 2018, Intel Corporation. All rights reserved.\n\n");
130 
131   //
132   // Details Option
133   //
134   fprintf (stdout, "Options:\n");
135   fprintf (stdout, "  -o FileName, --outputfile FileName\n\
136                         File is FFS file to be created.\n");
137   fprintf (stdout, "  -t Type, --filetype Type\n\
138                         Type is one FV file type defined in PI spec, which is\n\
139                         EFI_FV_FILETYPE_RAW, EFI_FV_FILETYPE_FREEFORM,\n\
140                         EFI_FV_FILETYPE_SECURITY_CORE, EFI_FV_FILETYPE_PEIM,\n\
141                         EFI_FV_FILETYPE_PEI_CORE, EFI_FV_FILETYPE_DXE_CORE,\n\
142                         EFI_FV_FILETYPE_DRIVER, EFI_FV_FILETYPE_APPLICATION,\n\
143                         EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER,\n\
144                         EFI_FV_FILETYPE_SMM, EFI_FV_FILETYPE_SMM_CORE,\n\
145                         EFI_FV_FILETYPE_MM_STANDALONE,\n\
146                         EFI_FV_FILETYPE_MM_CORE_STANDALONE,\n\
147                         EFI_FV_FILETYPE_COMBINED_SMM_DXE, \n\
148                         EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE.\n");
149   fprintf (stdout, "  -g FileGuid, --fileguid FileGuid\n\
150                         FileGuid is one module guid.\n\
151                         Its format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n");
152   fprintf (stdout, "  -x, --fixed           Indicates that the file may not be moved\n\
153                         from its present location.\n");
154   fprintf (stdout, "  -s, --checksum        Indicates to calculate file checksum.\n");
155   fprintf (stdout, "  -a FileAlign, --align FileAlign\n\
156                         FileAlign points to file alignment, which only support\n\
157                         the following align: 1,2,4,8,16,128,512,1K,4K,32K,64K\n\
158                         128K,256K,512K,1M,2M,4M,8M,16M\n");
159   fprintf (stdout, "  -i SectionFile, --sectionfile SectionFile\n\
160                         Section file will be contained in this FFS file.\n");
161   fprintf (stdout, "  -oi SectionFile, --optionalsectionfile SectionFile\n\
162                         If the Section file exists, it will be contained in this FFS file, otherwise, it will be ignored.\n");
163   fprintf (stdout, "  -n SectionAlign, --sectionalign SectionAlign\n\
164                         SectionAlign points to section alignment, which support\n\
165                         the alignment scope 0~16M. If SectionAlign is specified\n\
166                         as 0, tool get alignment value from SectionFile. It is\n\
167                         specified together with sectionfile to point its\n\
168                         alignment in FFS file.\n");
169   fprintf (stdout, "  -v, --verbose         Turn on verbose output with informational messages.\n");
170   fprintf (stdout, "  -q, --quiet           Disable all messages except key message and fatal error\n");
171   fprintf (stdout, "  -d, --debug level     Enable debug messages, at input debug level.\n");
172   fprintf (stdout, "  --version             Show program's version number and exit.\n");
173   fprintf (stdout, "  -h, --help            Show this help message and exit.\n");
174 }
175 
176 STATIC
177 EFI_STATUS
StringtoAlignment(IN CHAR8 * AlignBuffer,OUT UINT32 * AlignNumber)178 StringtoAlignment (
179   IN  CHAR8  *AlignBuffer,
180   OUT UINT32 *AlignNumber
181   )
182 /*++
183 
184 Routine Description:
185 
186   Converts Align String to align value (1~16M).
187 
188 Arguments:
189 
190   AlignBuffer    - Pointer to Align string.
191   AlignNumber    - Pointer to Align value.
192 
193 Returns:
194 
195   EFI_SUCCESS             Successfully convert align string to align value.
196   EFI_INVALID_PARAMETER   Align string is invalid or align value is not in scope.
197 
198 --*/
199 {
200   UINT32 Index = 0;
201   //
202   // Check AlignBuffer
203   //
204   if (AlignBuffer == NULL) {
205     return EFI_INVALID_PARAMETER;
206   }
207   for (Index = 0; Index < sizeof (mAlignName) / sizeof (CHAR8 *); Index ++) {
208     if (stricmp (AlignBuffer, mAlignName [Index]) == 0) {
209       *AlignNumber = 1 << Index;
210       return EFI_SUCCESS;
211     }
212   }
213   return EFI_INVALID_PARAMETER;
214 }
215 
216 STATIC
217 UINT8
StringToType(IN CHAR8 * String)218 StringToType (
219   IN CHAR8 *String
220   )
221 /*++
222 
223 Routine Description:
224 
225   Converts File Type String to value.  EFI_FV_FILETYPE_ALL indicates that an
226   unrecognized file type was specified.
227 
228 Arguments:
229 
230   String    - File type string
231 
232 Returns:
233 
234   File Type Value
235 
236 --*/
237 {
238   UINT8 Index = 0;
239 
240   if (String == NULL) {
241     return EFI_FV_FILETYPE_ALL;
242   }
243 
244   for (Index = 0; Index < sizeof (mFfsFileType) / sizeof (CHAR8 *); Index ++) {
245     if (mFfsFileType [Index] != NULL && (stricmp (String, mFfsFileType [Index]) == 0)) {
246       return Index;
247     }
248   }
249   return EFI_FV_FILETYPE_ALL;
250 }
251 
252 STATIC
253 EFI_STATUS
GetSectionContents(IN CHAR8 ** InputFileName,IN UINT32 * InputFileAlign,IN UINT32 InputFileNum,IN EFI_FFS_FILE_ATTRIBUTES FfsAttrib,OUT UINT8 * FileBuffer,OUT UINT32 * BufferLength,OUT UINT32 * MaxAlignment,OUT UINT8 * PESectionNum)254 GetSectionContents (
255   IN  CHAR8                     **InputFileName,
256   IN  UINT32                    *InputFileAlign,
257   IN  UINT32                    InputFileNum,
258   IN  EFI_FFS_FILE_ATTRIBUTES   FfsAttrib,
259   OUT UINT8                     *FileBuffer,
260   OUT UINT32                    *BufferLength,
261   OUT UINT32                    *MaxAlignment,
262   OUT UINT8                     *PESectionNum
263   )
264 /*++
265 
266 Routine Description:
267 
268   Get the contents of all section files specified in InputFileName
269   into FileBuffer.
270 
271 Arguments:
272 
273   InputFileName  - Name of the input file.
274 
275   InputFileAlign - Alignment required by the input file data.
276 
277   InputFileNum   - Number of input files. Should be at least 1.
278 
279   FileBuffer     - Output buffer to contain data
280 
281   BufferLength   - On input, this is size of the FileBuffer.
282                    On output, this is the actual length of the data.
283 
284   MaxAlignment   - The max alignment required by all the input file datas.
285 
286   PeSectionNum   - Calculate the number of Pe/Te Section in this FFS file.
287 
288 Returns:
289 
290   EFI_SUCCESS on successful return
291   EFI_INVALID_PARAMETER if InputFileNum is less than 1 or BufferLength point is NULL.
292   EFI_ABORTED if unable to open input file.
293   EFI_BUFFER_TOO_SMALL FileBuffer is not enough to contain all file data.
294 --*/
295 {
296   UINT32                              Size;
297   UINT32                              Offset;
298   UINT32                              FileSize;
299   UINT32                              Index;
300   FILE                                *InFile;
301   EFI_FREEFORM_SUBTYPE_GUID_SECTION   *SectHeader;
302   EFI_COMMON_SECTION_HEADER2          TempSectHeader;
303   EFI_TE_IMAGE_HEADER                 TeHeader;
304   UINT32                              TeOffset;
305   EFI_GUID_DEFINED_SECTION            GuidSectHeader;
306   EFI_GUID_DEFINED_SECTION2           GuidSectHeader2;
307   UINT32                              HeaderSize;
308   UINT32                              MaxEncounteredAlignment;
309 
310   Size                    = 0;
311   Offset                  = 0;
312   TeOffset                = 0;
313   MaxEncounteredAlignment = 1;
314 
315   //
316   // Go through our array of file names and copy their contents
317   // to the output buffer.
318   //
319   for (Index = 0; Index < InputFileNum; Index++) {
320     //
321     // make sure section ends on a DWORD boundary
322     //
323     while ((Size & 0x03) != 0) {
324       Size++;
325     }
326 
327     //
328     // Open file and read contents
329     //
330     InFile = fopen (LongFilePath (InputFileName[Index]), "rb");
331     if (InFile == NULL) {
332       Error (NULL, 0, 0001, "Error opening file", InputFileName[Index]);
333       return EFI_ABORTED;
334     }
335 
336     fseek (InFile, 0, SEEK_END);
337     FileSize = ftell (InFile);
338     fseek (InFile, 0, SEEK_SET);
339     DebugMsg (NULL, 0, 9, "Input section files",
340               "the input section name is %s and the size is %u bytes", InputFileName[Index], (unsigned) FileSize);
341 
342     //
343     // Check this section is Te/Pe section, and Calculate the numbers of Te/Pe section.
344     //
345     TeOffset = 0;
346     if (FileSize >= MAX_FFS_SIZE) {
347       HeaderSize = sizeof (EFI_COMMON_SECTION_HEADER2);
348     } else {
349       HeaderSize = sizeof (EFI_COMMON_SECTION_HEADER);
350     }
351     fread (&TempSectHeader, 1, HeaderSize, InFile);
352     if (TempSectHeader.Type == EFI_SECTION_TE) {
353       (*PESectionNum) ++;
354       fread (&TeHeader, 1, sizeof (TeHeader), InFile);
355       if (TeHeader.Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) {
356         TeOffset = TeHeader.StrippedSize - sizeof (TeHeader);
357       }
358     } else if (TempSectHeader.Type == EFI_SECTION_PE32) {
359       (*PESectionNum) ++;
360     } else if (TempSectHeader.Type == EFI_SECTION_GUID_DEFINED) {
361       fseek (InFile, 0, SEEK_SET);
362       if (FileSize >= MAX_SECTION_SIZE) {
363         fread (&GuidSectHeader2, 1, sizeof (GuidSectHeader2), InFile);
364         if ((GuidSectHeader2.Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) {
365           HeaderSize = GuidSectHeader2.DataOffset;
366         }
367       } else {
368         fread (&GuidSectHeader, 1, sizeof (GuidSectHeader), InFile);
369         if ((GuidSectHeader.Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) {
370           HeaderSize = GuidSectHeader.DataOffset;
371         }
372       }
373       (*PESectionNum) ++;
374     } else if (TempSectHeader.Type == EFI_SECTION_COMPRESSION ||
375                TempSectHeader.Type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) {
376       //
377       // for the encapsulated section, assume it contains Pe/Te section
378       //
379       (*PESectionNum) ++;
380     }
381 
382     fseek (InFile, 0, SEEK_SET);
383 
384     //
385     // Revert TeOffset to the converse value relative to Alignment
386     // This is to assure the original PeImage Header at Alignment.
387     //
388     if ((TeOffset != 0) && (InputFileAlign [Index] != 0)) {
389       TeOffset = InputFileAlign [Index] - (TeOffset % InputFileAlign [Index]);
390       TeOffset = TeOffset % InputFileAlign [Index];
391     }
392 
393     //
394     // make sure section data meet its alignment requirement by adding one pad section.
395     // But the different sections have the different section header. Necessary or not?
396     // Based on section type to adjust offset? Todo
397     //
398     if ((InputFileAlign [Index] != 0) && (((Size + HeaderSize + TeOffset) % InputFileAlign [Index]) != 0)) {
399       Offset = (Size + sizeof (EFI_COMMON_SECTION_HEADER) + HeaderSize + TeOffset + InputFileAlign [Index] - 1) & ~(InputFileAlign [Index] - 1);
400       Offset = Offset - Size - HeaderSize - TeOffset;
401 
402       if (FileBuffer != NULL && ((Size + Offset) < *BufferLength)) {
403         //
404         // The maximal alignment is 64K, the raw section size must be less than 0xffffff
405         //
406         memset (FileBuffer + Size, 0, Offset);
407         SectHeader                        = (EFI_FREEFORM_SUBTYPE_GUID_SECTION *) (FileBuffer + Size);
408         SectHeader->CommonHeader.Size[0]  = (UINT8) (Offset & 0xff);
409         SectHeader->CommonHeader.Size[1]  = (UINT8) ((Offset & 0xff00) >> 8);
410         SectHeader->CommonHeader.Size[2]  = (UINT8) ((Offset & 0xff0000) >> 16);
411 
412         //
413         // Only add a special reducible padding section if
414         // - this FFS has the FFS_ATTRIB_FIXED attribute,
415         // - none of the preceding sections have alignment requirements,
416         // - the size of the padding is sufficient for the
417         //   EFI_SECTION_FREEFORM_SUBTYPE_GUID header.
418         //
419         if ((FfsAttrib & FFS_ATTRIB_FIXED) != 0 &&
420             MaxEncounteredAlignment <= 1 &&
421             Offset >= sizeof (EFI_FREEFORM_SUBTYPE_GUID_SECTION)) {
422           SectHeader->CommonHeader.Type   = EFI_SECTION_FREEFORM_SUBTYPE_GUID;
423           SectHeader->SubTypeGuid         = mEfiFfsSectionAlignmentPaddingGuid;
424         } else {
425           SectHeader->CommonHeader.Type   = EFI_SECTION_RAW;
426         }
427       }
428       DebugMsg (NULL, 0, 9, "Pad raw section for section data alignment",
429                 "Pad Raw section size is %u", (unsigned) Offset);
430 
431       Size = Size + Offset;
432     }
433 
434     //
435     // Get the Max alignment of all input file datas
436     //
437     if (MaxEncounteredAlignment < InputFileAlign [Index]) {
438       MaxEncounteredAlignment = InputFileAlign [Index];
439     }
440 
441     //
442     // Now read the contents of the file into the buffer
443     // Buffer must be enough to contain the file content.
444     //
445     if ((FileSize > 0) && (FileBuffer != NULL) && ((Size + FileSize) <= *BufferLength)) {
446       if (fread (FileBuffer + Size, (size_t) FileSize, 1, InFile) != 1) {
447         Error (NULL, 0, 0004, "Error reading file", InputFileName[Index]);
448         fclose (InFile);
449         return EFI_ABORTED;
450       }
451     }
452 
453     fclose (InFile);
454     Size += FileSize;
455   }
456 
457   *MaxAlignment = MaxEncounteredAlignment;
458 
459   //
460   // Set the actual length of the data.
461   //
462   if (Size > *BufferLength) {
463     *BufferLength = Size;
464     return EFI_BUFFER_TOO_SMALL;
465   } else {
466     *BufferLength = Size;
467     return EFI_SUCCESS;
468   }
469 }
470 
471 EFI_STATUS
FfsRebaseImageRead(IN VOID * FileHandle,IN UINTN FileOffset,IN OUT UINT32 * ReadSize,OUT VOID * Buffer)472 FfsRebaseImageRead (
473     IN      VOID    *FileHandle,
474     IN      UINTN   FileOffset,
475     IN OUT  UINT32  *ReadSize,
476     OUT     VOID    *Buffer
477     )
478   /*++
479 
480     Routine Description:
481 
482     Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file
483 
484     Arguments:
485 
486    FileHandle - The handle to the PE/COFF file
487 
488    FileOffset - The offset, in bytes, into the file to read
489 
490    ReadSize   - The number of bytes to read from the file starting at FileOffset
491 
492    Buffer     - A pointer to the buffer to read the data into.
493 
494    Returns:
495 
496    EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset
497 
498    --*/
499 {
500   CHAR8   *Destination8;
501   CHAR8   *Source8;
502   UINT32  Length;
503 
504   Destination8  = Buffer;
505   Source8       = (CHAR8 *) ((UINTN) FileHandle + FileOffset);
506   Length        = *ReadSize;
507   while (Length--) {
508     *(Destination8++) = *(Source8++);
509   }
510 
511   return EFI_SUCCESS;
512 }
513 
514 STATIC
515 EFI_STATUS
GetAlignmentFromFile(char * InFile,UINT32 * Alignment)516 GetAlignmentFromFile(char *InFile, UINT32 *Alignment)
517   /*++
518     InFile is input file for getting alignment
519     return the alignment
520     --*/
521 {
522   FILE                           *InFileHandle;
523   UINT8                          *PeFileBuffer;
524   UINTN                          PeFileSize;
525   UINT32                         CurSecHdrSize;
526   PE_COFF_LOADER_IMAGE_CONTEXT   ImageContext;
527   EFI_COMMON_SECTION_HEADER      *CommonHeader;
528   EFI_STATUS                     Status;
529 
530   InFileHandle        = NULL;
531   PeFileBuffer        = NULL;
532   *Alignment          = 0;
533 
534   memset (&ImageContext, 0, sizeof (ImageContext));
535 
536   InFileHandle = fopen(LongFilePath(InFile), "rb");
537   if (InFileHandle == NULL){
538     Error (NULL, 0, 0001, "Error opening file", InFile);
539     return EFI_ABORTED;
540   }
541   PeFileSize = _filelength (fileno(InFileHandle));
542   PeFileBuffer = (UINT8 *) malloc (PeFileSize);
543   if (PeFileBuffer == NULL) {
544     fclose (InFileHandle);
545     Error(NULL, 0, 4001, "Resource", "memory cannot be allocated  of %s", InFileHandle);
546     return EFI_OUT_OF_RESOURCES;
547   }
548   fread (PeFileBuffer, sizeof (UINT8), PeFileSize, InFileHandle);
549   fclose (InFileHandle);
550   CommonHeader = (EFI_COMMON_SECTION_HEADER *) PeFileBuffer;
551   CurSecHdrSize = GetSectionHeaderLength(CommonHeader);
552   ImageContext.Handle = (VOID *) ((UINTN)PeFileBuffer + CurSecHdrSize);
553   ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)FfsRebaseImageRead;
554   Status               = PeCoffLoaderGetImageInfo(&ImageContext);
555   if (EFI_ERROR (Status)) {
556     Error (NULL, 0, 3000, "Invalid PeImage", "The input file is %s and return status is %x", InFile, (int) Status);
557     return Status;
558    }
559   *Alignment = ImageContext.SectionAlignment;
560   // Free the allocated memory resource
561   if (PeFileBuffer != NULL) {
562     free (PeFileBuffer);
563     PeFileBuffer = NULL;
564   }
565   return EFI_SUCCESS;
566 }
567 
568 int
main(int argc,CHAR8 * argv[])569 main (
570   int   argc,
571   CHAR8 *argv[]
572   )
573 /*++
574 
575 Routine Description:
576 
577   Main function.
578 
579 Arguments:
580 
581   argc - Number of command line parameters.
582   argv - Array of pointers to parameter strings.
583 
584 Returns:
585   STATUS_SUCCESS - Utility exits successfully.
586   STATUS_ERROR   - Some error occurred during execution.
587 
588 --*/
589 {
590   EFI_STATUS              Status;
591   EFI_FFS_FILE_ATTRIBUTES FfsAttrib;
592   UINT32                  FfsAlign;
593   EFI_FV_FILETYPE         FfsFiletype;
594   CHAR8                   *OutputFileName;
595   EFI_GUID                FileGuid = {0};
596   UINT32                  InputFileNum;
597   UINT32                  *InputFileAlign;
598   CHAR8                   **InputFileName;
599   UINT8                   *FileBuffer;
600   UINT32                  FileSize;
601   UINT32                  MaxAlignment;
602   EFI_FFS_FILE_HEADER2    FfsFileHeader;
603   FILE                    *FfsFile;
604   UINT32                  Index;
605   UINT64                  LogLevel;
606   UINT8                   PeSectionNum;
607   UINT32                  HeaderSize;
608   UINT32                  Alignment;
609   //
610   // Workaround for static code checkers.
611   // Ensures the size of 'AlignmentBuffer' can hold all the digits of an
612   // unsigned 32-bit integer plus the size unit character.
613   //
614   CHAR8                   AlignmentBuffer[16];
615 
616   //
617   // Init local variables
618   //
619   LogLevel       = 0;
620   Index          = 0;
621   FfsAttrib      = 0;
622   FfsAlign       = 0;
623   FfsFiletype    = EFI_FV_FILETYPE_ALL;
624   OutputFileName = NULL;
625   InputFileNum   = 0;
626   InputFileName  = NULL;
627   InputFileAlign = NULL;
628   FileBuffer     = NULL;
629   FileSize       = 0;
630   MaxAlignment   = 1;
631   FfsFile        = NULL;
632   Status         = EFI_SUCCESS;
633   PeSectionNum   = 0;
634 
635   SetUtilityName (UTILITY_NAME);
636 
637   if (argc == 1) {
638     Error (NULL, 0, 1001, "Missing options", "no options input");
639     Usage ();
640     return STATUS_ERROR;
641   }
642 
643   //
644   // Parse command line
645   //
646   argc --;
647   argv ++;
648 
649   if ((stricmp (argv[0], "-h") == 0) || (stricmp (argv[0], "--help") == 0)) {
650     Version ();
651     Usage ();
652     return STATUS_SUCCESS;
653   }
654 
655   if (stricmp (argv[0], "--version") == 0) {
656     Version ();
657     return STATUS_SUCCESS;
658   }
659 
660   while (argc > 0) {
661     if ((stricmp (argv[0], "-t") == 0) || (stricmp (argv[0], "--filetype") == 0)) {
662       if (argv[1] == NULL || argv[1][0] == '-') {
663         Error (NULL, 0, 1003, "Invalid option value", "file type is missing for -t option");
664         goto Finish;
665       }
666       FfsFiletype = StringToType (argv[1]);
667       if (FfsFiletype == EFI_FV_FILETYPE_ALL) {
668         Error (NULL, 0, 1003, "Invalid option value", "%s is not a valid file type", argv[1]);
669         goto Finish;
670       }
671       argc -= 2;
672       argv += 2;
673       continue;
674     }
675 
676     if ((stricmp (argv[0], "-o") == 0) || (stricmp (argv[0], "--outputfile") == 0)) {
677       if (argv[1] == NULL || argv[1][0] == '-') {
678         Error (NULL, 0, 1003, "Invalid option value", "Output file is missing for -o option");
679         goto Finish;
680       }
681       OutputFileName = argv[1];
682       argc -= 2;
683       argv += 2;
684       continue;
685     }
686 
687     if ((stricmp (argv[0], "-g") == 0) || (stricmp (argv[0], "--fileguid") == 0)) {
688       Status = StringToGuid (argv[1], &FileGuid);
689       if (EFI_ERROR (Status)) {
690         Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
691         goto Finish;
692       }
693       argc -= 2;
694       argv += 2;
695       continue;
696     }
697 
698     if ((stricmp (argv[0], "-x") == 0) || (stricmp (argv[0], "--fixed") == 0)) {
699       FfsAttrib |= FFS_ATTRIB_FIXED;
700       argc -= 1;
701       argv += 1;
702       continue;
703     }
704 
705     if ((stricmp (argv[0], "-s") == 0) || (stricmp (argv[0], "--checksum") == 0)) {
706       FfsAttrib |= FFS_ATTRIB_CHECKSUM;
707       argc -= 1;
708       argv += 1;
709       continue;
710     }
711 
712     if ((stricmp (argv[0], "-a") == 0) || (stricmp (argv[0], "--align") == 0)) {
713       if (argv[1] == NULL || argv[1][0] == '-') {
714         Error (NULL, 0, 1003, "Invalid option value", "Align value is missing for -a option");
715         goto Finish;
716       }
717       for (Index = 0; Index < sizeof (mFfsValidAlignName) / sizeof (CHAR8 *); Index ++) {
718         if (stricmp (argv[1], mFfsValidAlignName[Index]) == 0) {
719           break;
720         }
721       }
722       if (Index == sizeof (mFfsValidAlignName) / sizeof (CHAR8 *)) {
723         if ((stricmp (argv[1], "1") == 0) || (stricmp (argv[1], "2") == 0) || (stricmp (argv[1], "4") == 0)) {
724           //
725           // 1, 2, 4 byte alignment same to 8 byte alignment
726           //
727           Index = 0;
728         } else {
729           Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
730           goto Finish;
731         }
732       }
733       FfsAlign = Index;
734       argc -= 2;
735       argv += 2;
736       continue;
737     }
738 
739     if ((stricmp (argv[0], "-oi") == 0) || (stricmp (argv[0], "--optionalsectionfile") == 0) || (stricmp (argv[0], "-i") == 0) || (stricmp (argv[0], "--sectionfile") == 0)) {
740       //
741       // Get Input file name and its alignment
742       //
743       if (argv[1] == NULL || argv[1][0] == '-') {
744         Error (NULL, 0, 1003, "Invalid option value", "input section file is missing for -i option");
745         goto Finish;
746       }
747       if ((stricmp (argv[0], "-oi") == 0) || (stricmp (argv[0], "--optionalsectionfile") == 0) ){
748         if (-1 == access(argv[1] , 0)){
749           Warning(NULL, 0, 0001, "File is not found.", argv[1]);
750           argc -= 2;
751           argv += 2;
752           continue;
753         }
754       }
755       //
756       // Allocate Input file name buffer and its alignment buffer.
757       //
758       if ((InputFileNum == 0) && (InputFileName == NULL)) {
759         InputFileName = (CHAR8 **) malloc (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *));
760         if (InputFileName == NULL) {
761           Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
762           return STATUS_ERROR;
763         }
764         memset (InputFileName, 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *)));
765 
766         InputFileAlign = (UINT32 *) malloc (MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32));
767         if (InputFileAlign == NULL) {
768           Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
769           free (InputFileName);
770           return STATUS_ERROR;
771         }
772         memset (InputFileAlign, 0, MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32));
773       } else if (InputFileNum % MAXIMUM_INPUT_FILE_NUM == 0) {
774         //
775         // InputFileName and alignment buffer too small, need to realloc
776         //
777         InputFileName = (CHAR8 **) realloc (
778                                     InputFileName,
779                                     (InputFileNum + MAXIMUM_INPUT_FILE_NUM) * sizeof (CHAR8 *)
780                                     );
781 
782         if (InputFileName == NULL) {
783           Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
784           free (InputFileAlign);
785           return STATUS_ERROR;
786         }
787         memset (&(InputFileName[InputFileNum]), 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *)));
788 
789         InputFileAlign = (UINT32 *) realloc (
790                                     InputFileAlign,
791                                     (InputFileNum + MAXIMUM_INPUT_FILE_NUM) * sizeof (UINT32)
792                                     );
793 
794         if (InputFileAlign == NULL) {
795           Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
796           free (InputFileName);
797           return STATUS_ERROR;
798         }
799         memset (&(InputFileAlign[InputFileNum]), 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32)));
800       }
801 
802       InputFileName[InputFileNum] = argv[1];
803       argc -= 2;
804       argv += 2;
805 
806       if (argc <= 0) {
807         InputFileNum ++;
808         break;
809       }
810 
811       //
812       // Section File alignment requirement
813       //
814       if ((stricmp (argv[0], "-n") == 0) || (stricmp (argv[0], "--sectionalign") == 0)) {
815         if ((argv[1] != NULL) && (stricmp("0", argv[1]) == 0)) {
816           Status = GetAlignmentFromFile(InputFileName[InputFileNum], &Alignment);
817           if (EFI_ERROR(Status)) {
818             Error (NULL, 0, 1003, "Fail to get Alignment from %s", InputFileName[InputFileNum]);
819             goto Finish;
820           }
821           if (Alignment < 0x400){
822             sprintf (AlignmentBuffer, "%d", Alignment);
823           }
824           else if (Alignment >= 0x400) {
825             if (Alignment >= 0x100000) {
826               sprintf (AlignmentBuffer, "%dM", Alignment/0x100000);
827             } else {
828               sprintf (AlignmentBuffer, "%dK", Alignment/0x400);
829             }
830           }
831           Status = StringtoAlignment (AlignmentBuffer, &(InputFileAlign[InputFileNum]));
832         }
833         else {
834           Status = StringtoAlignment (argv[1], &(InputFileAlign[InputFileNum]));
835         }
836         if (EFI_ERROR (Status)) {
837           Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
838           goto Finish;
839         }
840         argc -= 2;
841         argv += 2;
842       }
843       InputFileNum ++;
844       continue;
845     }
846 
847     if ((stricmp (argv[0], "-n") == 0) || (stricmp (argv[0], "--sectionalign") == 0)) {
848       Error (NULL, 0, 1000, "Unknown option", "SectionAlign option must be specified with section file.");
849       goto Finish;
850     }
851 
852     if ((stricmp (argv[0], "-v") == 0) || (stricmp (argv[0], "--verbose") == 0)) {
853       SetPrintLevel (VERBOSE_LOG_LEVEL);
854       VerboseMsg ("Verbose output Mode Set!");
855       argc --;
856       argv ++;
857       continue;
858     }
859 
860     if ((stricmp (argv[0], "-q") == 0) || (stricmp (argv[0], "--quiet") == 0)) {
861       SetPrintLevel (KEY_LOG_LEVEL);
862       KeyMsg ("Quiet output Mode Set!");
863       argc --;
864       argv ++;
865       continue;
866     }
867 
868     if ((stricmp (argv[0], "-d") == 0) || (stricmp (argv[0], "--debug") == 0)) {
869       Status = AsciiStringToUint64 (argv[1], FALSE, &LogLevel);
870       if (EFI_ERROR (Status)) {
871         Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]);
872         goto Finish;
873       }
874       if (LogLevel > 9) {
875         Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, current input level is %d", (int) LogLevel);
876         goto Finish;
877       }
878       SetPrintLevel (LogLevel);
879       DebugMsg (NULL, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv[1]);
880       argc -= 2;
881       argv += 2;
882       continue;
883     }
884 
885     Error (NULL, 0, 1000, "Unknown option", "%s", argv[0]);
886     goto Finish;
887   }
888 
889   VerboseMsg ("%s tool start.", UTILITY_NAME);
890 
891   //
892   // Check the complete input parameters.
893   //
894   if (FfsFiletype == EFI_FV_FILETYPE_ALL) {
895     Error (NULL, 0, 1001, "Missing option", "filetype");
896     goto Finish;
897   }
898 
899   if (CompareGuid (&FileGuid, &mZeroGuid) == 0) {
900     Error (NULL, 0, 1001, "Missing option", "fileguid");
901     goto Finish;
902   }
903 
904   if (InputFileNum == 0) {
905     Error (NULL, 0, 1001, "Missing option", "Input files");
906     goto Finish;
907   }
908 
909   //
910   // Output input parameter information
911   //
912   VerboseMsg ("Fv File type is %s", mFfsFileType [FfsFiletype]);
913   VerboseMsg ("Output file name is %s", OutputFileName);
914   VerboseMsg ("FFS File Guid is %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X",
915                 (unsigned) FileGuid.Data1,
916                 FileGuid.Data2,
917                 FileGuid.Data3,
918                 FileGuid.Data4[0],
919                 FileGuid.Data4[1],
920                 FileGuid.Data4[2],
921                 FileGuid.Data4[3],
922                 FileGuid.Data4[4],
923                 FileGuid.Data4[5],
924                 FileGuid.Data4[6],
925                 FileGuid.Data4[7]);
926   if ((FfsAttrib & FFS_ATTRIB_FIXED) != 0) {
927     VerboseMsg ("FFS File has the fixed file attribute");
928   }
929   if ((FfsAttrib & FFS_ATTRIB_CHECKSUM) != 0) {
930     VerboseMsg ("FFS File requires the checksum of the whole file");
931   }
932   VerboseMsg ("FFS file alignment is %s", mFfsValidAlignName[FfsAlign]);
933   for (Index = 0; Index < InputFileNum; Index ++) {
934     if (InputFileAlign[Index] == 0) {
935       //
936       // Minimum alignment is 1 byte.
937       //
938       InputFileAlign[Index] = 1;
939     }
940     VerboseMsg ("the %dth input section name is %s and section alignment is %u", Index, InputFileName[Index], (unsigned) InputFileAlign[Index]);
941   }
942 
943   //
944   // Calculate the size of all input section files.
945   //
946   Status = GetSectionContents (
947              InputFileName,
948              InputFileAlign,
949              InputFileNum,
950              FfsAttrib,
951              FileBuffer,
952              &FileSize,
953              &MaxAlignment,
954              &PeSectionNum
955              );
956 
957   if ((FfsFiletype == EFI_FV_FILETYPE_SECURITY_CORE ||
958       FfsFiletype == EFI_FV_FILETYPE_PEI_CORE ||
959       FfsFiletype == EFI_FV_FILETYPE_DXE_CORE) && (PeSectionNum != 1)) {
960     Error (NULL, 0, 2000, "Invalid parameter", "Fv File type %s must have one and only one Pe or Te section, but %u Pe/Te section are input", mFfsFileType [FfsFiletype], PeSectionNum);
961     goto Finish;
962   }
963 
964   if ((FfsFiletype == EFI_FV_FILETYPE_PEIM ||
965       FfsFiletype == EFI_FV_FILETYPE_DRIVER ||
966       FfsFiletype == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER ||
967       FfsFiletype == EFI_FV_FILETYPE_APPLICATION) && (PeSectionNum < 1)) {
968     Error (NULL, 0, 2000, "Invalid parameter", "Fv File type %s must have at least one Pe or Te section, but no Pe/Te section is input", mFfsFileType [FfsFiletype]);
969     goto Finish;
970   }
971 
972   if (Status == EFI_BUFFER_TOO_SMALL) {
973     FileBuffer = (UINT8 *) malloc (FileSize);
974     if (FileBuffer == NULL) {
975       Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
976       goto Finish;
977     }
978     memset (FileBuffer, 0, FileSize);
979 
980     //
981     // read all input file contents into a buffer
982     //
983     Status = GetSectionContents (
984                InputFileName,
985                InputFileAlign,
986                InputFileNum,
987                FfsAttrib,
988                FileBuffer,
989                &FileSize,
990                &MaxAlignment,
991                &PeSectionNum
992                );
993   }
994 
995   if (EFI_ERROR (Status)) {
996     goto Finish;
997   }
998 
999   if (FileBuffer == NULL && FileSize != 0) {
1000     Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!");
1001     goto Finish;
1002   }
1003 
1004   //
1005   // Create Ffs file header.
1006   //
1007   memset (&FfsFileHeader, 0, sizeof (EFI_FFS_FILE_HEADER2));
1008   memcpy (&FfsFileHeader.Name, &FileGuid, sizeof (EFI_GUID));
1009   FfsFileHeader.Type       = FfsFiletype;
1010   //
1011   // Update FFS Alignment based on the max alignment required by input section files
1012   //
1013   VerboseMsg ("the max alignment of all input sections is %u", (unsigned) MaxAlignment);
1014   for (Index = 0; Index < sizeof (mFfsValidAlign) / sizeof (UINT32) - 1; Index ++) {
1015     if ((MaxAlignment > mFfsValidAlign [Index]) && (MaxAlignment <= mFfsValidAlign [Index + 1])) {
1016       break;
1017     }
1018   }
1019   if (FfsAlign < Index) {
1020     FfsAlign = Index;
1021   }
1022   VerboseMsg ("the alignment of the generated FFS file is %u", (unsigned) mFfsValidAlign [FfsAlign + 1]);
1023 
1024   //
1025   // Now FileSize includes the EFI_FFS_FILE_HEADER
1026   //
1027   if (FileSize + sizeof (EFI_FFS_FILE_HEADER) >= MAX_FFS_SIZE) {
1028     HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
1029     FileSize += sizeof (EFI_FFS_FILE_HEADER2);
1030     FfsFileHeader.ExtendedSize = FileSize;
1031     memset(FfsFileHeader.Size, 0, sizeof (UINT8) * 3);
1032     FfsAttrib |= FFS_ATTRIB_LARGE_FILE;
1033   } else {
1034     HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
1035     FileSize += sizeof (EFI_FFS_FILE_HEADER);
1036     FfsFileHeader.Size[0]  = (UINT8) (FileSize & 0xFF);
1037     FfsFileHeader.Size[1]  = (UINT8) ((FileSize & 0xFF00) >> 8);
1038     FfsFileHeader.Size[2]  = (UINT8) ((FileSize & 0xFF0000) >> 16);
1039   }
1040   VerboseMsg ("the size of the generated FFS file is %u bytes", (unsigned) FileSize);
1041 
1042   //FfsAlign larger than 7, set FFS_ATTRIB_DATA_ALIGNMENT2
1043   if (FfsAlign < 8) {
1044     FfsFileHeader.Attributes = (EFI_FFS_FILE_ATTRIBUTES) (FfsAttrib | (FfsAlign << 3));
1045   } else {
1046     FfsFileHeader.Attributes = (EFI_FFS_FILE_ATTRIBUTES) (FfsAttrib | ((FfsAlign & 0x7) << 3) | FFS_ATTRIB_DATA_ALIGNMENT2);
1047   }
1048 
1049   //
1050   // Fill in checksums and state, these must be zero for checksumming
1051   //
1052   // FileHeader.IntegrityCheck.Checksum.Header = 0;
1053   // FileHeader.IntegrityCheck.Checksum.File = 0;
1054   // FileHeader.State = 0;
1055   //
1056   FfsFileHeader.IntegrityCheck.Checksum.Header = CalculateChecksum8 (
1057                                                    (UINT8 *) &FfsFileHeader,
1058                                                    HeaderSize
1059                                                    );
1060 
1061   if (FfsFileHeader.Attributes & FFS_ATTRIB_CHECKSUM) {
1062     //
1063     // Ffs header checksum = zero, so only need to calculate ffs body.
1064     //
1065     FfsFileHeader.IntegrityCheck.Checksum.File = CalculateChecksum8 (
1066                                                    FileBuffer,
1067                                                    FileSize - HeaderSize
1068                                                    );
1069   } else {
1070     FfsFileHeader.IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;
1071   }
1072 
1073   FfsFileHeader.State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID;
1074 
1075   //
1076   // Open output file to write ffs data.
1077   //
1078   if (OutputFileName != NULL) {
1079     remove(OutputFileName);
1080     FfsFile = fopen (LongFilePath (OutputFileName), "wb");
1081     if (FfsFile == NULL) {
1082       Error (NULL, 0, 0001, "Error opening file", OutputFileName);
1083       goto Finish;
1084     }
1085     //
1086     // write header
1087     //
1088     fwrite (&FfsFileHeader, 1, HeaderSize, FfsFile);
1089     //
1090     // write data
1091     //
1092     if (FileBuffer != NULL) {
1093       fwrite (FileBuffer, 1, FileSize - HeaderSize, FfsFile);
1094     }
1095 
1096     fclose (FfsFile);
1097   }
1098 
1099 Finish:
1100   if (InputFileName != NULL) {
1101     free (InputFileName);
1102   }
1103   if (InputFileAlign != NULL) {
1104     free (InputFileAlign);
1105   }
1106   if (FileBuffer != NULL) {
1107     free (FileBuffer);
1108   }
1109   //
1110   // If any errors were reported via the standard error reporting
1111   // routines, then the status has been saved. Get the value and
1112   // return it to the caller.
1113   //
1114   VerboseMsg ("%s tool done with return code is 0x%x.", UTILITY_NAME, GetUtilityStatus ());
1115 
1116   return GetUtilityStatus ();
1117 }
1118