/** @file This file contains functions required to generate a Firmware File System file. Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #ifndef __GNUC__ # ifdef VBOX // RUNTIME_FUNCTION typedef conflicts; just use EFI headers # else #include # endif #include #include #include #endif #ifdef __GNUC__ #include #endif #include #include #include #include #include #include #include #include "CommonLib.h" #include "ParseInf.h" #include "EfiUtilityMsgs.h" #include "FvLib.h" #include "PeCoffLib.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 "EFI_FV_FILETYPE_MM_STANDALONE", // 0x0E "EFI_FV_FILETYPE_MM_CORE_STANDALONE" // 0x0F }; STATIC CHAR8 *mAlignName[] = { "1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1K", "2K", "4K", "8K", "16K", "32K", "64K", "128K", "256K", "512K", "1M", "2M", "4M", "8M", "16M" }; STATIC CHAR8 *mFfsValidAlignName[] = { "8", "16", "128", "512", "1K", "4K", "32K", "64K", "128K","256K", "512K", "1M", "2M", "4M", "8M", "16M" }; STATIC UINT32 mFfsValidAlign[] = {0, 8, 16, 128, 512, 1024, 4096, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216}; 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 - 2018, 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_MM_STANDALONE,\n\ EFI_FV_FILETYPE_MM_CORE_STANDALONE,\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\ 128K,256K,512K,1M,2M,4M,8M,16M\n"); fprintf (stdout, " -i SectionFile, --sectionfile SectionFile\n\ Section file will be contained in this FFS file.\n"); fprintf (stdout, " -oi SectionFile, --optionalsectionfile SectionFile\n\ If the Section file exists, it will be contained in this FFS file, otherwise, it will be ignored.\n"); fprintf (stdout, " -n SectionAlign, --sectionalign SectionAlign\n\ SectionAlign points to section alignment, which support\n\ the alignment scope 0~16M. If SectionAlign is specified\n\ as 0, tool get alignment value from SectionFile. It is\n\ specified together with sectionfile to point its\n\ 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~16M). 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; } } EFI_STATUS FfsRebaseImageRead ( IN VOID *FileHandle, IN UINTN FileOffset, IN OUT UINT32 *ReadSize, OUT VOID *Buffer ) /*++ Routine Description: Support routine for the PE/COFF Loader that reads a buffer from a PE/COFF file Arguments: FileHandle - The handle to the PE/COFF file FileOffset - The offset, in bytes, into the file to read ReadSize - The number of bytes to read from the file starting at FileOffset Buffer - A pointer to the buffer to read the data into. Returns: EFI_SUCCESS - ReadSize bytes of data were read into Buffer from the PE/COFF file starting at FileOffset --*/ { CHAR8 *Destination8; CHAR8 *Source8; UINT32 Length; Destination8 = Buffer; Source8 = (CHAR8 *) ((UINTN) FileHandle + FileOffset); Length = *ReadSize; while (Length--) { *(Destination8++) = *(Source8++); } return EFI_SUCCESS; } STATIC EFI_STATUS GetAlignmentFromFile(char *InFile, UINT32 *Alignment) /*++ InFile is input file for getting alignment return the alignment --*/ { FILE *InFileHandle; UINT8 *PeFileBuffer; UINTN PeFileSize; UINT32 CurSecHdrSize; PE_COFF_LOADER_IMAGE_CONTEXT ImageContext; EFI_COMMON_SECTION_HEADER *CommonHeader; EFI_STATUS Status; InFileHandle = NULL; PeFileBuffer = NULL; *Alignment = 0; memset (&ImageContext, 0, sizeof (ImageContext)); InFileHandle = fopen(LongFilePath(InFile), "rb"); if (InFileHandle == NULL){ Error (NULL, 0, 0001, "Error opening file", InFile); return EFI_ABORTED; } PeFileSize = _filelength (fileno(InFileHandle)); PeFileBuffer = (UINT8 *) malloc (PeFileSize); if (PeFileBuffer == NULL) { fclose (InFileHandle); Error(NULL, 0, 4001, "Resource", "memory cannot be allocated of %s", InFileHandle); return EFI_OUT_OF_RESOURCES; } fread (PeFileBuffer, sizeof (UINT8), PeFileSize, InFileHandle); fclose (InFileHandle); CommonHeader = (EFI_COMMON_SECTION_HEADER *) PeFileBuffer; CurSecHdrSize = GetSectionHeaderLength(CommonHeader); ImageContext.Handle = (VOID *) ((UINTN)PeFileBuffer + CurSecHdrSize); ImageContext.ImageRead = (PE_COFF_LOADER_READ_FILE)FfsRebaseImageRead; Status = PeCoffLoaderGetImageInfo(&ImageContext); if (EFI_ERROR (Status)) { Error (NULL, 0, 3000, "Invalid PeImage", "The input file is %s and return status is %x", InFile, (int) Status); return Status; } *Alignment = ImageContext.SectionAlignment; // Free the allocated memory resource if (PeFileBuffer != NULL) { free (PeFileBuffer); PeFileBuffer = NULL; } 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; UINT32 Alignment; // // Workaround for static code checkers. // Ensures the size of 'AlignmentBuffer' can hold all the digits of an // unsigned 32-bit integer plus the size unit character. // CHAR8 AlignmentBuffer[16]; // // 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], "-oi") == 0) || (stricmp (argv[0], "--optionalsectionfile") == 0) || (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; } if ((stricmp (argv[0], "-oi") == 0) || (stricmp (argv[0], "--optionalsectionfile") == 0) ){ if (-1 == access(argv[1] , 0)){ Warning(NULL, 0, 0001, "File is not found.", argv[1]); argc -= 2; argv += 2; continue; } } // // 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)) { if ((argv[1] != NULL) && (stricmp("0", argv[1]) == 0)) { Status = GetAlignmentFromFile(InputFileName[InputFileNum], &Alignment); if (EFI_ERROR(Status)) { Error (NULL, 0, 1003, "Fail to get Alignment from %s", InputFileName[InputFileNum]); goto Finish; } if (Alignment < 0x400){ sprintf (AlignmentBuffer, "%d", Alignment); } else if (Alignment >= 0x100000) { sprintf (AlignmentBuffer, "%dM", Alignment/0x100000); } else { sprintf (AlignmentBuffer, "%dK", Alignment/0x400); } Status = StringtoAlignment (AlignmentBuffer, &(InputFileAlign[InputFileNum])); } else { 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", "%s", argv[0]); goto Finish; } VerboseMsg ("%s tool start.", UTILITY_NAME); // // Check the complete input parameters. // 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; } if (FileBuffer == NULL && FileSize != 0) { Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!"); 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); //FfsAlign larger than 7, set FFS_ATTRIB_DATA_ALIGNMENT2 if (FfsAlign < 8) { FfsFileHeader.Attributes = (EFI_FFS_FILE_ATTRIBUTES) (FfsAttrib | (FfsAlign << 3)); } else { FfsFileHeader.Attributes = (EFI_FFS_FILE_ATTRIBUTES) (FfsAttrib | ((FfsAlign & 0x7) << 3) | FFS_ATTRIB_DATA_ALIGNMENT2); } // // 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. // if (OutputFileName != NULL) { 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 // if (FileBuffer != NULL) { 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 (); }