| /** @file | |
| This file contains functions required to generate a Firmware File System file. | |
| Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR> | |
| This program and the accompanying materials | |
| are licensed and made available under the terms and conditions of the BSD License | |
| which accompanies this distribution. The full text of the license may be found at | |
| http://opensource.org/licenses/bsd-license.php | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. | |
| **/ | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <Common/UefiBaseTypes.h> | |
| #include <Common/PiFirmwareFile.h> | |
| #include <IndustryStandard/PeImage.h> | |
| #include <Guid/FfsSectionAlignmentPadding.h> | |
| #include "CommonLib.h" | |
| #include "ParseInf.h" | |
| #include "EfiUtilityMsgs.h" | |
| #define UTILITY_NAME "GenFfs" | |
| #define UTILITY_MAJOR_VERSION 0 | |
| #define UTILITY_MINOR_VERSION 1 | |
| STATIC CHAR8 *mFfsFileType[] = { | |
| NULL, // 0x00 | |
| "EFI_FV_FILETYPE_RAW", // 0x01 | |
| "EFI_FV_FILETYPE_FREEFORM", // 0x02 | |
| "EFI_FV_FILETYPE_SECURITY_CORE", // 0x03 | |
| "EFI_FV_FILETYPE_PEI_CORE", // 0x04 | |
| "EFI_FV_FILETYPE_DXE_CORE", // 0x05 | |
| "EFI_FV_FILETYPE_PEIM", // 0x06 | |
| "EFI_FV_FILETYPE_DRIVER", // 0x07 | |
| "EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER", // 0x08 | |
| "EFI_FV_FILETYPE_APPLICATION", // 0x09 | |
| "EFI_FV_FILETYPE_SMM", // 0x0A | |
| "EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE",// 0x0B | |
| "EFI_FV_FILETYPE_COMBINED_SMM_DXE", // 0x0C | |
| "EFI_FV_FILETYPE_SMM_CORE" // 0x0D | |
| }; | |
| STATIC CHAR8 *mAlignName[] = { | |
| "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", | |
| "1K", "2K", "4K", "8K", "16K", "32K", "64K" | |
| }; | |
| STATIC CHAR8 *mFfsValidAlignName[] = { | |
| "8", "16", "128", "512", "1K", "4K", "32K", "64K" | |
| }; | |
| STATIC UINT32 mFfsValidAlign[] = {0, 8, 16, 128, 512, 1024, 4096, 32768, 65536}; | |
| STATIC EFI_GUID mZeroGuid = {0}; | |
| STATIC EFI_GUID mEfiFfsSectionAlignmentPaddingGuid = EFI_FFS_SECTION_ALIGNMENT_PADDING_GUID; | |
| STATIC | |
| VOID | |
| Version ( | |
| VOID | |
| ) | |
| /*++ | |
| Routine Description: | |
| Print out version information for this utility. | |
| Arguments: | |
| None | |
| Returns: | |
| None | |
| --*/ | |
| { | |
| fprintf (stdout, "%s Version %d.%d %s \n", UTILITY_NAME, UTILITY_MAJOR_VERSION, UTILITY_MINOR_VERSION, __BUILD_VERSION); | |
| } | |
| STATIC | |
| VOID | |
| Usage ( | |
| VOID | |
| ) | |
| /*++ | |
| Routine Description: | |
| Print Error / Help message. | |
| Arguments: | |
| VOID | |
| Returns: | |
| None | |
| --*/ | |
| { | |
| // | |
| // Summary usage | |
| // | |
| fprintf (stdout, "\nUsage: %s [options]\n\n", UTILITY_NAME); | |
| // | |
| // Copyright declaration | |
| // | |
| fprintf (stdout, "Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.\n\n"); | |
| // | |
| // Details Option | |
| // | |
| fprintf (stdout, "Options:\n"); | |
| fprintf (stdout, " -o FileName, --outputfile FileName\n\ | |
| File is FFS file to be created.\n"); | |
| fprintf (stdout, " -t Type, --filetype Type\n\ | |
| Type is one FV file type defined in PI spec, which is\n\ | |
| EFI_FV_FILETYPE_RAW, EFI_FV_FILETYPE_FREEFORM,\n\ | |
| EFI_FV_FILETYPE_SECURITY_CORE, EFI_FV_FILETYPE_PEIM,\n\ | |
| EFI_FV_FILETYPE_PEI_CORE, EFI_FV_FILETYPE_DXE_CORE,\n\ | |
| EFI_FV_FILETYPE_DRIVER, EFI_FV_FILETYPE_APPLICATION,\n\ | |
| EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER,\n\ | |
| EFI_FV_FILETYPE_SMM, EFI_FV_FILETYPE_SMM_CORE,\n\ | |
| EFI_FV_FILETYPE_COMBINED_SMM_DXE, \n\ | |
| EFI_FV_FILETYPE_FIRMWARE_VOLUME_IMAGE.\n"); | |
| fprintf (stdout, " -g FileGuid, --fileguid FileGuid\n\ | |
| FileGuid is one module guid.\n\ | |
| Its format is xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\n"); | |
| fprintf (stdout, " -x, --fixed Indicates that the file may not be moved\n\ | |
| from its present location.\n"); | |
| fprintf (stdout, " -s, --checksum Indicates to calculate file checksum.\n"); | |
| fprintf (stdout, " -a FileAlign, --align FileAlign\n\ | |
| FileAlign points to file alignment, which only support\n\ | |
| the following align: 1,2,4,8,16,128,512,1K,4K,32K,64K\n"); | |
| fprintf (stdout, " -i SectionFile, --sectionfile SectionFile\n\ | |
| Section file will be contained in this FFS file.\n"); | |
| fprintf (stdout, " -n SectionAlign, --sectionalign SectionAlign\n\ | |
| SectionAlign points to section alignment, which support\n\ | |
| the alignment scope 1~64K. It is specified together\n\ | |
| with sectionfile to point its alignment in FFS file.\n"); | |
| fprintf (stdout, " -v, --verbose Turn on verbose output with informational messages.\n"); | |
| fprintf (stdout, " -q, --quiet Disable all messages except key message and fatal error\n"); | |
| fprintf (stdout, " -d, --debug level Enable debug messages, at input debug level.\n"); | |
| fprintf (stdout, " --version Show program's version number and exit.\n"); | |
| fprintf (stdout, " -h, --help Show this help message and exit.\n"); | |
| } | |
| STATIC | |
| EFI_STATUS | |
| StringtoAlignment ( | |
| IN CHAR8 *AlignBuffer, | |
| OUT UINT32 *AlignNumber | |
| ) | |
| /*++ | |
| Routine Description: | |
| Converts Align String to align value (1~64K). | |
| Arguments: | |
| AlignBuffer - Pointer to Align string. | |
| AlignNumber - Pointer to Align value. | |
| Returns: | |
| EFI_SUCCESS Successfully convert align string to align value. | |
| EFI_INVALID_PARAMETER Align string is invalid or align value is not in scope. | |
| --*/ | |
| { | |
| UINT32 Index = 0; | |
| // | |
| // Check AlignBuffer | |
| // | |
| if (AlignBuffer == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| for (Index = 0; Index < sizeof (mAlignName) / sizeof (CHAR8 *); Index ++) { | |
| if (stricmp (AlignBuffer, mAlignName [Index]) == 0) { | |
| *AlignNumber = 1 << Index; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| STATIC | |
| UINT8 | |
| StringToType ( | |
| IN CHAR8 *String | |
| ) | |
| /*++ | |
| Routine Description: | |
| Converts File Type String to value. EFI_FV_FILETYPE_ALL indicates that an | |
| unrecognized file type was specified. | |
| Arguments: | |
| String - File type string | |
| Returns: | |
| File Type Value | |
| --*/ | |
| { | |
| UINT8 Index = 0; | |
| if (String == NULL) { | |
| return EFI_FV_FILETYPE_ALL; | |
| } | |
| for (Index = 0; Index < sizeof (mFfsFileType) / sizeof (CHAR8 *); Index ++) { | |
| if (mFfsFileType [Index] != NULL && (stricmp (String, mFfsFileType [Index]) == 0)) { | |
| return Index; | |
| } | |
| } | |
| return EFI_FV_FILETYPE_ALL; | |
| } | |
| STATIC | |
| 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 | |
| ) | |
| /*++ | |
| Routine Description: | |
| Get the contents of all section files specified in InputFileName | |
| into FileBuffer. | |
| Arguments: | |
| InputFileName - Name of the input file. | |
| InputFileAlign - Alignment required by the input file data. | |
| InputFileNum - Number of input files. Should be at least 1. | |
| FileBuffer - Output buffer to contain data | |
| BufferLength - On input, this is size of the FileBuffer. | |
| On output, this is the actual length of the data. | |
| MaxAlignment - The max alignment required by all the input file datas. | |
| PeSectionNum - Calculate the number of Pe/Te Section in this FFS file. | |
| Returns: | |
| EFI_SUCCESS on successful return | |
| EFI_INVALID_PARAMETER if InputFileNum is less than 1 or BufferLength point is NULL. | |
| EFI_ABORTED if unable to open input file. | |
| EFI_BUFFER_TOO_SMALL FileBuffer is not enough to contain all file data. | |
| --*/ | |
| { | |
| UINT32 Size; | |
| UINT32 Offset; | |
| UINT32 FileSize; | |
| UINT32 Index; | |
| FILE *InFile; | |
| EFI_FREEFORM_SUBTYPE_GUID_SECTION *SectHeader; | |
| EFI_COMMON_SECTION_HEADER2 TempSectHeader; | |
| EFI_TE_IMAGE_HEADER TeHeader; | |
| UINT32 TeOffset; | |
| EFI_GUID_DEFINED_SECTION GuidSectHeader; | |
| EFI_GUID_DEFINED_SECTION2 GuidSectHeader2; | |
| UINT32 HeaderSize; | |
| UINT32 MaxEncounteredAlignment; | |
| Size = 0; | |
| Offset = 0; | |
| TeOffset = 0; | |
| MaxEncounteredAlignment = 1; | |
| // | |
| // Go through our array of file names and copy their contents | |
| // to the output buffer. | |
| // | |
| for (Index = 0; Index < InputFileNum; Index++) { | |
| // | |
| // make sure section ends on a DWORD boundary | |
| // | |
| while ((Size & 0x03) != 0) { | |
| Size++; | |
| } | |
| // | |
| // Open file and read contents | |
| // | |
| InFile = fopen (LongFilePath (InputFileName[Index]), "rb"); | |
| if (InFile == NULL) { | |
| Error (NULL, 0, 0001, "Error opening file", InputFileName[Index]); | |
| return EFI_ABORTED; | |
| } | |
| fseek (InFile, 0, SEEK_END); | |
| FileSize = ftell (InFile); | |
| fseek (InFile, 0, SEEK_SET); | |
| DebugMsg (NULL, 0, 9, "Input section files", | |
| "the input section name is %s and the size is %u bytes", InputFileName[Index], (unsigned) FileSize); | |
| // | |
| // Check this section is Te/Pe section, and Calculate the numbers of Te/Pe section. | |
| // | |
| TeOffset = 0; | |
| if (FileSize >= MAX_FFS_SIZE) { | |
| HeaderSize = sizeof (EFI_COMMON_SECTION_HEADER2); | |
| } else { | |
| HeaderSize = sizeof (EFI_COMMON_SECTION_HEADER); | |
| } | |
| fread (&TempSectHeader, 1, HeaderSize, InFile); | |
| if (TempSectHeader.Type == EFI_SECTION_TE) { | |
| (*PESectionNum) ++; | |
| fread (&TeHeader, 1, sizeof (TeHeader), InFile); | |
| if (TeHeader.Signature == EFI_TE_IMAGE_HEADER_SIGNATURE) { | |
| TeOffset = TeHeader.StrippedSize - sizeof (TeHeader); | |
| } | |
| } else if (TempSectHeader.Type == EFI_SECTION_PE32) { | |
| (*PESectionNum) ++; | |
| } else if (TempSectHeader.Type == EFI_SECTION_GUID_DEFINED) { | |
| fseek (InFile, 0, SEEK_SET); | |
| if (FileSize >= MAX_SECTION_SIZE) { | |
| fread (&GuidSectHeader2, 1, sizeof (GuidSectHeader2), InFile); | |
| if ((GuidSectHeader2.Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) { | |
| HeaderSize = GuidSectHeader2.DataOffset; | |
| } | |
| } else { | |
| fread (&GuidSectHeader, 1, sizeof (GuidSectHeader), InFile); | |
| if ((GuidSectHeader.Attributes & EFI_GUIDED_SECTION_PROCESSING_REQUIRED) == 0) { | |
| HeaderSize = GuidSectHeader.DataOffset; | |
| } | |
| } | |
| (*PESectionNum) ++; | |
| } else if (TempSectHeader.Type == EFI_SECTION_COMPRESSION || | |
| TempSectHeader.Type == EFI_SECTION_FIRMWARE_VOLUME_IMAGE) { | |
| // | |
| // for the encapsulated section, assume it contains Pe/Te section | |
| // | |
| (*PESectionNum) ++; | |
| } | |
| fseek (InFile, 0, SEEK_SET); | |
| // | |
| // Revert TeOffset to the converse value relative to Alignment | |
| // This is to assure the original PeImage Header at Alignment. | |
| // | |
| if ((TeOffset != 0) && (InputFileAlign [Index] != 0)) { | |
| TeOffset = InputFileAlign [Index] - (TeOffset % InputFileAlign [Index]); | |
| TeOffset = TeOffset % InputFileAlign [Index]; | |
| } | |
| // | |
| // make sure section data meet its alignment requirement by adding one pad section. | |
| // But the different sections have the different section header. Necessary or not? | |
| // Based on section type to adjust offset? Todo | |
| // | |
| if ((InputFileAlign [Index] != 0) && (((Size + HeaderSize + TeOffset) % InputFileAlign [Index]) != 0)) { | |
| Offset = (Size + sizeof (EFI_COMMON_SECTION_HEADER) + HeaderSize + TeOffset + InputFileAlign [Index] - 1) & ~(InputFileAlign [Index] - 1); | |
| Offset = Offset - Size - HeaderSize - TeOffset; | |
| if (FileBuffer != NULL && ((Size + Offset) < *BufferLength)) { | |
| // | |
| // The maximal alignment is 64K, the raw section size must be less than 0xffffff | |
| // | |
| memset (FileBuffer + Size, 0, Offset); | |
| SectHeader = (EFI_FREEFORM_SUBTYPE_GUID_SECTION *) (FileBuffer + Size); | |
| SectHeader->CommonHeader.Size[0] = (UINT8) (Offset & 0xff); | |
| SectHeader->CommonHeader.Size[1] = (UINT8) ((Offset & 0xff00) >> 8); | |
| SectHeader->CommonHeader.Size[2] = (UINT8) ((Offset & 0xff0000) >> 16); | |
| // | |
| // Only add a special reducible padding section if | |
| // - this FFS has the FFS_ATTRIB_FIXED attribute, | |
| // - none of the preceding sections have alignment requirements, | |
| // - the size of the padding is sufficient for the | |
| // EFI_SECTION_FREEFORM_SUBTYPE_GUID header. | |
| // | |
| if ((FfsAttrib & FFS_ATTRIB_FIXED) != 0 && | |
| MaxEncounteredAlignment <= 1 && | |
| Offset >= sizeof (EFI_FREEFORM_SUBTYPE_GUID_SECTION)) { | |
| SectHeader->CommonHeader.Type = EFI_SECTION_FREEFORM_SUBTYPE_GUID; | |
| SectHeader->SubTypeGuid = mEfiFfsSectionAlignmentPaddingGuid; | |
| } else { | |
| SectHeader->CommonHeader.Type = EFI_SECTION_RAW; | |
| } | |
| } | |
| DebugMsg (NULL, 0, 9, "Pad raw section for section data alignment", | |
| "Pad Raw section size is %u", (unsigned) Offset); | |
| Size = Size + Offset; | |
| } | |
| // | |
| // Get the Max alignment of all input file datas | |
| // | |
| if (MaxEncounteredAlignment < InputFileAlign [Index]) { | |
| MaxEncounteredAlignment = InputFileAlign [Index]; | |
| } | |
| // | |
| // Now read the contents of the file into the buffer | |
| // Buffer must be enough to contain the file content. | |
| // | |
| if ((FileSize > 0) && (FileBuffer != NULL) && ((Size + FileSize) <= *BufferLength)) { | |
| if (fread (FileBuffer + Size, (size_t) FileSize, 1, InFile) != 1) { | |
| Error (NULL, 0, 0004, "Error reading file", InputFileName[Index]); | |
| fclose (InFile); | |
| return EFI_ABORTED; | |
| } | |
| } | |
| fclose (InFile); | |
| Size += FileSize; | |
| } | |
| *MaxAlignment = MaxEncounteredAlignment; | |
| // | |
| // Set the actual length of the data. | |
| // | |
| if (Size > *BufferLength) { | |
| *BufferLength = Size; | |
| return EFI_BUFFER_TOO_SMALL; | |
| } else { | |
| *BufferLength = Size; | |
| return EFI_SUCCESS; | |
| } | |
| } | |
| int | |
| main ( | |
| int argc, | |
| CHAR8 *argv[] | |
| ) | |
| /*++ | |
| Routine Description: | |
| Main function. | |
| Arguments: | |
| argc - Number of command line parameters. | |
| argv - Array of pointers to parameter strings. | |
| Returns: | |
| STATUS_SUCCESS - Utility exits successfully. | |
| STATUS_ERROR - Some error occurred during execution. | |
| --*/ | |
| { | |
| EFI_STATUS Status; | |
| EFI_FFS_FILE_ATTRIBUTES FfsAttrib; | |
| UINT32 FfsAlign; | |
| EFI_FV_FILETYPE FfsFiletype; | |
| CHAR8 *OutputFileName; | |
| EFI_GUID FileGuid = {0}; | |
| UINT32 InputFileNum; | |
| UINT32 *InputFileAlign; | |
| CHAR8 **InputFileName; | |
| UINT8 *FileBuffer; | |
| UINT32 FileSize; | |
| UINT32 MaxAlignment; | |
| EFI_FFS_FILE_HEADER2 FfsFileHeader; | |
| FILE *FfsFile; | |
| UINT32 Index; | |
| UINT64 LogLevel; | |
| UINT8 PeSectionNum; | |
| UINT32 HeaderSize; | |
| // | |
| // Init local variables | |
| // | |
| LogLevel = 0; | |
| Index = 0; | |
| FfsAttrib = 0; | |
| FfsAlign = 0; | |
| FfsFiletype = EFI_FV_FILETYPE_ALL; | |
| OutputFileName = NULL; | |
| InputFileNum = 0; | |
| InputFileName = NULL; | |
| InputFileAlign = NULL; | |
| FileBuffer = NULL; | |
| FileSize = 0; | |
| MaxAlignment = 1; | |
| FfsFile = NULL; | |
| Status = EFI_SUCCESS; | |
| PeSectionNum = 0; | |
| SetUtilityName (UTILITY_NAME); | |
| if (argc == 1) { | |
| Error (NULL, 0, 1001, "Missing options", "no options input"); | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Parse command line | |
| // | |
| argc --; | |
| argv ++; | |
| if ((stricmp (argv[0], "-h") == 0) || (stricmp (argv[0], "--help") == 0)) { | |
| Version (); | |
| Usage (); | |
| return STATUS_SUCCESS; | |
| } | |
| if (stricmp (argv[0], "--version") == 0) { | |
| Version (); | |
| return STATUS_SUCCESS; | |
| } | |
| while (argc > 0) { | |
| if ((stricmp (argv[0], "-t") == 0) || (stricmp (argv[0], "--filetype") == 0)) { | |
| if (argv[1] == NULL || argv[1][0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "file type is missing for -t option"); | |
| goto Finish; | |
| } | |
| FfsFiletype = StringToType (argv[1]); | |
| if (FfsFiletype == EFI_FV_FILETYPE_ALL) { | |
| Error (NULL, 0, 1003, "Invalid option value", "%s is not a valid file type", argv[1]); | |
| goto Finish; | |
| } | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-o") == 0) || (stricmp (argv[0], "--outputfile") == 0)) { | |
| if (argv[1] == NULL || argv[1][0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "Output file is missing for -o option"); | |
| goto Finish; | |
| } | |
| OutputFileName = argv[1]; | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-g") == 0) || (stricmp (argv[0], "--fileguid") == 0)) { | |
| Status = StringToGuid (argv[1], &FileGuid); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]); | |
| goto Finish; | |
| } | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-x") == 0) || (stricmp (argv[0], "--fixed") == 0)) { | |
| FfsAttrib |= FFS_ATTRIB_FIXED; | |
| argc -= 1; | |
| argv += 1; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-s") == 0) || (stricmp (argv[0], "--checksum") == 0)) { | |
| FfsAttrib |= FFS_ATTRIB_CHECKSUM; | |
| argc -= 1; | |
| argv += 1; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-a") == 0) || (stricmp (argv[0], "--align") == 0)) { | |
| if (argv[1] == NULL || argv[1][0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "Align value is missing for -a option"); | |
| goto Finish; | |
| } | |
| for (Index = 0; Index < sizeof (mFfsValidAlignName) / sizeof (CHAR8 *); Index ++) { | |
| if (stricmp (argv[1], mFfsValidAlignName[Index]) == 0) { | |
| break; | |
| } | |
| } | |
| if (Index == sizeof (mFfsValidAlignName) / sizeof (CHAR8 *)) { | |
| if ((stricmp (argv[1], "1") == 0) || (stricmp (argv[1], "2") == 0) || (stricmp (argv[1], "4") == 0)) { | |
| // | |
| // 1, 2, 4 byte alignment same to 8 byte alignment | |
| // | |
| Index = 0; | |
| } else { | |
| Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]); | |
| goto Finish; | |
| } | |
| } | |
| FfsAlign = Index; | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-i") == 0) || (stricmp (argv[0], "--sectionfile") == 0)) { | |
| // | |
| // Get Input file name and its alignment | |
| // | |
| if (argv[1] == NULL || argv[1][0] == '-') { | |
| Error (NULL, 0, 1003, "Invalid option value", "input section file is missing for -i option"); | |
| goto Finish; | |
| } | |
| // | |
| // Allocate Input file name buffer and its alignment buffer. | |
| // | |
| if ((InputFileNum == 0) && (InputFileName == NULL)) { | |
| InputFileName = (CHAR8 **) malloc (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *)); | |
| if (InputFileName == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!"); | |
| return STATUS_ERROR; | |
| } | |
| memset (InputFileName, 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *))); | |
| InputFileAlign = (UINT32 *) malloc (MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32)); | |
| if (InputFileAlign == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!"); | |
| free (InputFileName); | |
| return STATUS_ERROR; | |
| } | |
| memset (InputFileAlign, 0, MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32)); | |
| } else if (InputFileNum % MAXIMUM_INPUT_FILE_NUM == 0) { | |
| // | |
| // InputFileName and alignment buffer too small, need to realloc | |
| // | |
| InputFileName = (CHAR8 **) realloc ( | |
| InputFileName, | |
| (InputFileNum + MAXIMUM_INPUT_FILE_NUM) * sizeof (CHAR8 *) | |
| ); | |
| if (InputFileName == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!"); | |
| free (InputFileAlign); | |
| return STATUS_ERROR; | |
| } | |
| memset (&(InputFileName[InputFileNum]), 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (CHAR8 *))); | |
| InputFileAlign = (UINT32 *) realloc ( | |
| InputFileAlign, | |
| (InputFileNum + MAXIMUM_INPUT_FILE_NUM) * sizeof (UINT32) | |
| ); | |
| if (InputFileAlign == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!"); | |
| free (InputFileName); | |
| return STATUS_ERROR; | |
| } | |
| memset (&(InputFileAlign[InputFileNum]), 0, (MAXIMUM_INPUT_FILE_NUM * sizeof (UINT32))); | |
| } | |
| InputFileName[InputFileNum] = argv[1]; | |
| argc -= 2; | |
| argv += 2; | |
| if (argc <= 0) { | |
| InputFileNum ++; | |
| break; | |
| } | |
| // | |
| // Section File alignment requirement | |
| // | |
| if ((stricmp (argv[0], "-n") == 0) || (stricmp (argv[0], "--sectionalign") == 0)) { | |
| Status = StringtoAlignment (argv[1], &(InputFileAlign[InputFileNum])); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]); | |
| goto Finish; | |
| } | |
| argc -= 2; | |
| argv += 2; | |
| } | |
| InputFileNum ++; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-n") == 0) || (stricmp (argv[0], "--sectionalign") == 0)) { | |
| Error (NULL, 0, 1000, "Unknown option", "SectionAlign option must be specified with section file."); | |
| goto Finish; | |
| } | |
| if ((stricmp (argv[0], "-v") == 0) || (stricmp (argv[0], "--verbose") == 0)) { | |
| SetPrintLevel (VERBOSE_LOG_LEVEL); | |
| VerboseMsg ("Verbose output Mode Set!"); | |
| argc --; | |
| argv ++; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-q") == 0) || (stricmp (argv[0], "--quiet") == 0)) { | |
| SetPrintLevel (KEY_LOG_LEVEL); | |
| KeyMsg ("Quiet output Mode Set!"); | |
| argc --; | |
| argv ++; | |
| continue; | |
| } | |
| if ((stricmp (argv[0], "-d") == 0) || (stricmp (argv[0], "--debug") == 0)) { | |
| Status = AsciiStringToUint64 (argv[1], FALSE, &LogLevel); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 1003, "Invalid option value", "%s = %s", argv[0], argv[1]); | |
| goto Finish; | |
| } | |
| if (LogLevel > 9) { | |
| Error (NULL, 0, 1003, "Invalid option value", "Debug Level range is 0-9, current input level is %d", (int) LogLevel); | |
| goto Finish; | |
| } | |
| SetPrintLevel (LogLevel); | |
| DebugMsg (NULL, 0, 9, "Debug Mode Set", "Debug Output Mode Level %s is set!", argv[1]); | |
| argc -= 2; | |
| argv += 2; | |
| continue; | |
| } | |
| Error (NULL, 0, 1000, "Unknown option", argv[0]); | |
| goto Finish; | |
| } | |
| VerboseMsg ("%s tool start.", UTILITY_NAME); | |
| // | |
| // Check the complete input paramters. | |
| // | |
| if (FfsFiletype == EFI_FV_FILETYPE_ALL) { | |
| Error (NULL, 0, 1001, "Missing option", "filetype"); | |
| goto Finish; | |
| } | |
| if (CompareGuid (&FileGuid, &mZeroGuid) == 0) { | |
| Error (NULL, 0, 1001, "Missing option", "fileguid"); | |
| goto Finish; | |
| } | |
| if (InputFileNum == 0) { | |
| Error (NULL, 0, 1001, "Missing option", "Input files"); | |
| goto Finish; | |
| } | |
| // | |
| // Output input parameter information | |
| // | |
| VerboseMsg ("Fv File type is %s", mFfsFileType [FfsFiletype]); | |
| VerboseMsg ("Output file name is %s", OutputFileName); | |
| VerboseMsg ("FFS File Guid is %08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", | |
| (unsigned) FileGuid.Data1, | |
| FileGuid.Data2, | |
| FileGuid.Data3, | |
| FileGuid.Data4[0], | |
| FileGuid.Data4[1], | |
| FileGuid.Data4[2], | |
| FileGuid.Data4[3], | |
| FileGuid.Data4[4], | |
| FileGuid.Data4[5], | |
| FileGuid.Data4[6], | |
| FileGuid.Data4[7]); | |
| if ((FfsAttrib & FFS_ATTRIB_FIXED) != 0) { | |
| VerboseMsg ("FFS File has the fixed file attribute"); | |
| } | |
| if ((FfsAttrib & FFS_ATTRIB_CHECKSUM) != 0) { | |
| VerboseMsg ("FFS File requires the checksum of the whole file"); | |
| } | |
| VerboseMsg ("FFS file alignment is %s", mFfsValidAlignName[FfsAlign]); | |
| for (Index = 0; Index < InputFileNum; Index ++) { | |
| if (InputFileAlign[Index] == 0) { | |
| // | |
| // Minimum alignment is 1 byte. | |
| // | |
| InputFileAlign[Index] = 1; | |
| } | |
| VerboseMsg ("the %dth input section name is %s and section alignment is %u", Index, InputFileName[Index], (unsigned) InputFileAlign[Index]); | |
| } | |
| // | |
| // Calculate the size of all input section files. | |
| // | |
| Status = GetSectionContents ( | |
| InputFileName, | |
| InputFileAlign, | |
| InputFileNum, | |
| FfsAttrib, | |
| FileBuffer, | |
| &FileSize, | |
| &MaxAlignment, | |
| &PeSectionNum | |
| ); | |
| if ((FfsFiletype == EFI_FV_FILETYPE_SECURITY_CORE || | |
| FfsFiletype == EFI_FV_FILETYPE_PEI_CORE || | |
| FfsFiletype == EFI_FV_FILETYPE_DXE_CORE) && (PeSectionNum != 1)) { | |
| 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); | |
| goto Finish; | |
| } | |
| if ((FfsFiletype == EFI_FV_FILETYPE_PEIM || | |
| FfsFiletype == EFI_FV_FILETYPE_DRIVER || | |
| FfsFiletype == EFI_FV_FILETYPE_COMBINED_PEIM_DRIVER || | |
| FfsFiletype == EFI_FV_FILETYPE_APPLICATION) && (PeSectionNum < 1)) { | |
| 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]); | |
| goto Finish; | |
| } | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| FileBuffer = (UINT8 *) malloc (FileSize); | |
| if (FileBuffer == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!"); | |
| goto Finish; | |
| } | |
| memset (FileBuffer, 0, FileSize); | |
| // | |
| // read all input file contents into a buffer | |
| // | |
| Status = GetSectionContents ( | |
| InputFileName, | |
| InputFileAlign, | |
| InputFileNum, | |
| FfsAttrib, | |
| FileBuffer, | |
| &FileSize, | |
| &MaxAlignment, | |
| &PeSectionNum | |
| ); | |
| } | |
| if (EFI_ERROR (Status)) { | |
| goto Finish; | |
| } | |
| // | |
| // Create Ffs file header. | |
| // | |
| memset (&FfsFileHeader, 0, sizeof (EFI_FFS_FILE_HEADER2)); | |
| memcpy (&FfsFileHeader.Name, &FileGuid, sizeof (EFI_GUID)); | |
| FfsFileHeader.Type = FfsFiletype; | |
| // | |
| // Update FFS Alignment based on the max alignment required by input section files | |
| // | |
| VerboseMsg ("the max alignment of all input sections is %u", (unsigned) MaxAlignment); | |
| for (Index = 0; Index < sizeof (mFfsValidAlign) / sizeof (UINT32) - 1; Index ++) { | |
| if ((MaxAlignment > mFfsValidAlign [Index]) && (MaxAlignment <= mFfsValidAlign [Index + 1])) { | |
| break; | |
| } | |
| } | |
| if (FfsAlign < Index) { | |
| FfsAlign = Index; | |
| } | |
| VerboseMsg ("the alignment of the generated FFS file is %u", (unsigned) mFfsValidAlign [FfsAlign + 1]); | |
| // | |
| // Now FileSize includes the EFI_FFS_FILE_HEADER | |
| // | |
| if (FileSize + sizeof (EFI_FFS_FILE_HEADER) >= MAX_FFS_SIZE) { | |
| HeaderSize = sizeof (EFI_FFS_FILE_HEADER2); | |
| FileSize += sizeof (EFI_FFS_FILE_HEADER2); | |
| FfsFileHeader.ExtendedSize = FileSize; | |
| memset(FfsFileHeader.Size, 0, sizeof (UINT8) * 3); | |
| FfsAttrib |= FFS_ATTRIB_LARGE_FILE; | |
| } else { | |
| HeaderSize = sizeof (EFI_FFS_FILE_HEADER); | |
| FileSize += sizeof (EFI_FFS_FILE_HEADER); | |
| FfsFileHeader.Size[0] = (UINT8) (FileSize & 0xFF); | |
| FfsFileHeader.Size[1] = (UINT8) ((FileSize & 0xFF00) >> 8); | |
| FfsFileHeader.Size[2] = (UINT8) ((FileSize & 0xFF0000) >> 16); | |
| } | |
| VerboseMsg ("the size of the generated FFS file is %u bytes", (unsigned) FileSize); | |
| FfsFileHeader.Attributes = (EFI_FFS_FILE_ATTRIBUTES) (FfsAttrib | (FfsAlign << 3)); | |
| // | |
| // Fill in checksums and state, these must be zero for checksumming | |
| // | |
| // FileHeader.IntegrityCheck.Checksum.Header = 0; | |
| // FileHeader.IntegrityCheck.Checksum.File = 0; | |
| // FileHeader.State = 0; | |
| // | |
| FfsFileHeader.IntegrityCheck.Checksum.Header = CalculateChecksum8 ( | |
| (UINT8 *) &FfsFileHeader, | |
| HeaderSize | |
| ); | |
| if (FfsFileHeader.Attributes & FFS_ATTRIB_CHECKSUM) { | |
| // | |
| // Ffs header checksum = zero, so only need to calculate ffs body. | |
| // | |
| FfsFileHeader.IntegrityCheck.Checksum.File = CalculateChecksum8 ( | |
| FileBuffer, | |
| FileSize - HeaderSize | |
| ); | |
| } else { | |
| FfsFileHeader.IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM; | |
| } | |
| FfsFileHeader.State = EFI_FILE_HEADER_CONSTRUCTION | EFI_FILE_HEADER_VALID | EFI_FILE_DATA_VALID; | |
| // | |
| // Open output file to write ffs data. | |
| // | |
| remove(OutputFileName); | |
| FfsFile = fopen (LongFilePath (OutputFileName), "wb"); | |
| if (FfsFile == NULL) { | |
| Error (NULL, 0, 0001, "Error opening file", OutputFileName); | |
| goto Finish; | |
| } | |
| // | |
| // write header | |
| // | |
| fwrite (&FfsFileHeader, 1, HeaderSize, FfsFile); | |
| // | |
| // write data | |
| // | |
| fwrite (FileBuffer, 1, FileSize - HeaderSize, FfsFile); | |
| fclose (FfsFile); | |
| Finish: | |
| if (InputFileName != NULL) { | |
| free (InputFileName); | |
| } | |
| if (InputFileAlign != NULL) { | |
| free (InputFileAlign); | |
| } | |
| if (FileBuffer != NULL) { | |
| free (FileBuffer); | |
| } | |
| // | |
| // If any errors were reported via the standard error reporting | |
| // routines, then the status has been saved. Get the value and | |
| // return it to the caller. | |
| // | |
| VerboseMsg ("%s tool done with return code is 0x%x.", UTILITY_NAME, GetUtilityStatus ()); | |
| return GetUtilityStatus (); | |
| } |