| /** @file | |
| Utility program to create an EFI option ROM image from binary and EFI PE32 files. | |
| Copyright (c) 1999 - 2018, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "EfiUtilityMsgs.h" | |
| #include "ParseInf.h" | |
| #include "EfiRom.h" | |
| UINT64 DebugLevel = 0; | |
| int | |
| main ( | |
| int Argc, | |
| char *Argv[] | |
| ) | |
| /*++ | |
| Routine Description: | |
| Given an EFI image filename, create a ROM-able image by creating an option | |
| ROM header and PCI data structure, filling them in, and then writing the | |
| option ROM header + PCI data structure + EFI image out to the output file. | |
| Arguments: | |
| Argc - standard C main() argument count | |
| Argv - standard C main() argument list | |
| Returns: | |
| 0 success | |
| non-zero otherwise | |
| --*/ | |
| { | |
| CHAR8 *Ext; | |
| FILE *FptrOut; | |
| UINT32 Status; | |
| FILE_LIST *FList; | |
| UINT32 TotalSize; | |
| UINT32 Size; | |
| CHAR8 *Ptr0; | |
| SetUtilityName(UTILITY_NAME); | |
| Status = STATUS_SUCCESS; | |
| FptrOut = NULL; | |
| // | |
| // Parse the command line arguments | |
| // | |
| if (ParseCommandLine (Argc, Argv, &mOptions)) { | |
| return STATUS_ERROR; | |
| } | |
| if (mOptions.Quiet) { | |
| SetPrintLevel(40); | |
| } else if (mOptions.Verbose) { | |
| SetPrintLevel(15); | |
| } else if (mOptions.Debug) { | |
| SetPrintLevel(DebugLevel); | |
| } | |
| if (mOptions.Verbose) { | |
| VerboseMsg("%s tool start.\n", UTILITY_NAME); | |
| } | |
| // | |
| // If dumping an image, then do that and quit | |
| // | |
| if (mOptions.DumpOption == 1) { | |
| if (mOptions.FileList != NULL) { | |
| if ((Ptr0 = strstr ((CONST CHAR8 *) mOptions.FileList->FileName, DEFAULT_OUTPUT_EXTENSION)) != NULL) { | |
| DumpImage (mOptions.FileList); | |
| goto BailOut; | |
| } else { | |
| Error (NULL, 0, 1002, "No PciRom input file", "No *.rom input file"); | |
| goto BailOut; | |
| } | |
| } | |
| } | |
| // | |
| // Determine the output filename. Either what they specified on | |
| // the command line, or the first input filename with a different extension. | |
| // | |
| if (!mOptions.OutFileName[0]) { | |
| if (mOptions.FileList != NULL) { | |
| if (strlen (mOptions.FileList->FileName) >= MAX_PATH) { | |
| Status = STATUS_ERROR; | |
| Error (NULL, 0, 2000, "Invalid parameter", "Input file name is too long - %s.", mOptions.FileList->FileName); | |
| goto BailOut; | |
| } | |
| strncpy (mOptions.OutFileName, mOptions.FileList->FileName, MAX_PATH - 1); | |
| mOptions.OutFileName[MAX_PATH - 1] = 0; | |
| // | |
| // Find the last . on the line and replace the filename extension with | |
| // the default | |
| // | |
| Ext = mOptions.OutFileName + strlen (mOptions.OutFileName) - 1; | |
| while (Ext >= mOptions.OutFileName) { | |
| if ((*Ext == '.') || (*Ext == '\\')) { | |
| break; | |
| } | |
| Ext--; | |
| } | |
| // | |
| // If dot here, then insert extension here, otherwise append | |
| // | |
| if (*Ext != '.') { | |
| Ext = mOptions.OutFileName + strlen (mOptions.OutFileName); | |
| } | |
| strcpy (Ext, DEFAULT_OUTPUT_EXTENSION); | |
| } | |
| } | |
| // | |
| // Make sure we don't have the same filename for input and output files | |
| // | |
| for (FList = mOptions.FileList; FList != NULL; FList = FList->Next) { | |
| if (stricmp (mOptions.OutFileName, FList->FileName) == 0) { | |
| Status = STATUS_ERROR; | |
| Error (NULL, 0, 1002, "Invalid input parameter", "Input and output file names must be different - %s = %s.", FList->FileName, mOptions.OutFileName); | |
| goto BailOut; | |
| } | |
| } | |
| // | |
| // Now open our output file | |
| // | |
| if ((FptrOut = fopen (LongFilePath (mOptions.OutFileName), "wb")) == NULL) { | |
| Error (NULL, 0, 0001, "Error opening file", "Error opening file %s", mOptions.OutFileName); | |
| goto BailOut; | |
| } | |
| // | |
| // Process all our files | |
| // | |
| TotalSize = 0; | |
| for (FList = mOptions.FileList; FList != NULL; FList = FList->Next) { | |
| Size = 0; | |
| if ((FList->FileFlags & FILE_FLAG_EFI) != 0) { | |
| if (mOptions.Verbose) { | |
| VerboseMsg("Processing EFI file %s\n", FList->FileName); | |
| } | |
| Status = ProcessEfiFile (FptrOut, FList, mOptions.VendId, mOptions.DevIdList[0], &Size); | |
| } else if ((FList->FileFlags & FILE_FLAG_BINARY) !=0 ) { | |
| if (mOptions.Verbose) { | |
| VerboseMsg("Processing binary file %s\n", FList->FileName); | |
| } | |
| Status = ProcessBinFile (FptrOut, FList, &Size); | |
| } else { | |
| Error (NULL, 0, 2000, "Invalid parameter", "File type not specified, it must be either an EFI or binary file: %s.", FList->FileName); | |
| Status = STATUS_ERROR; | |
| } | |
| if (mOptions.Verbose) { | |
| VerboseMsg(" Output size = 0x%X\n", (unsigned) Size); | |
| } | |
| if (Status != STATUS_SUCCESS) { | |
| break; | |
| } | |
| TotalSize += Size; | |
| } | |
| // | |
| // Check total size | |
| // | |
| if (TotalSize > MAX_OPTION_ROM_SIZE) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Option ROM image size exceeds limit of 0x%X bytes.", MAX_OPTION_ROM_SIZE); | |
| Status = STATUS_ERROR; | |
| } | |
| BailOut: | |
| if (Status == STATUS_SUCCESS) { | |
| // | |
| // Clean up our file list | |
| // | |
| while (mOptions.FileList != NULL) { | |
| FList = mOptions.FileList->Next; | |
| free (mOptions.FileList); | |
| mOptions.FileList = FList; | |
| } | |
| // | |
| // Clean up device ID list | |
| // | |
| if (mOptions.DevIdList != NULL) { | |
| free (mOptions.DevIdList); | |
| } | |
| } | |
| if (FptrOut != NULL) { | |
| fclose (FptrOut); | |
| } | |
| if (mOptions.Verbose) { | |
| VerboseMsg("%s tool done with return code is 0x%x.\n", UTILITY_NAME, GetUtilityStatus ()); | |
| } | |
| return GetUtilityStatus (); | |
| } | |
| static | |
| int | |
| ProcessBinFile ( | |
| FILE *OutFptr, | |
| FILE_LIST *InFile, | |
| UINT32 *Size | |
| ) | |
| /*++ | |
| Routine Description: | |
| Process a binary input file. | |
| Arguments: | |
| OutFptr - file pointer to output binary ROM image file we're creating | |
| InFile - structure contains information on the binary file to process | |
| Size - pointer to where to return the size added to the output file | |
| Returns: | |
| 0 - successful | |
| --*/ | |
| { | |
| FILE *InFptr; | |
| UINT32 TotalSize; | |
| UINT32 FileSize; | |
| UINT8 *Buffer; | |
| UINT32 Status; | |
| PCI_EXPANSION_ROM_HEADER *RomHdr; | |
| PCI_DATA_STRUCTURE *PciDs23; | |
| PCI_3_0_DATA_STRUCTURE *PciDs30; | |
| UINT32 Index; | |
| UINT8 ByteCheckSum; | |
| UINT16 CodeType; | |
| PciDs23 = NULL; | |
| PciDs30 = NULL; | |
| Status = STATUS_SUCCESS; | |
| // | |
| // Try to open the input file | |
| // | |
| if ((InFptr = fopen (LongFilePath (InFile->FileName), "rb")) == NULL) { | |
| Error (NULL, 0, 0001, "Error opening file", "%s", InFile->FileName); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Seek to the end of the input file and get the file size. Then allocate | |
| // a buffer to read it in to. | |
| // | |
| fseek (InFptr, 0, SEEK_END); | |
| FileSize = ftell (InFptr); | |
| if (mOptions.Verbose) { | |
| VerboseMsg(" File size = 0x%X\n", (unsigned) FileSize); | |
| } | |
| fseek (InFptr, 0, SEEK_SET); | |
| Buffer = (UINT8 *) malloc (FileSize); | |
| if (Buffer == NULL) { | |
| Error (NULL, 0, 4003, "Resource", "memory cannot be allocated!"); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| if (fread (Buffer, FileSize, 1, InFptr) != 1) { | |
| Error (NULL, 0, 2000, "Invalid", "Failed to read all bytes from input file."); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Total size must be an even multiple of 512 bytes, and can't exceed | |
| // the option ROM image size. | |
| // | |
| TotalSize = FileSize; | |
| if (TotalSize & 0x1FF) { | |
| TotalSize = (TotalSize + 0x200) &~0x1ff; | |
| } | |
| if (TotalSize > MAX_OPTION_ROM_SIZE) { | |
| Error (NULL, 0, 3001, "Invalid", "Option ROM image %s size exceeds limit of 0x%X bytes.", InFile->FileName, MAX_OPTION_ROM_SIZE); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Return the size to the caller so they can keep track of the running total. | |
| // | |
| *Size = TotalSize; | |
| // | |
| // Crude check to make sure it's a legitimate ROM image | |
| // | |
| RomHdr = (PCI_EXPANSION_ROM_HEADER *) Buffer; | |
| if (RomHdr->Signature != PCI_EXPANSION_ROM_HEADER_SIGNATURE) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "ROM image file has an invalid ROM signature."); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Make sure the pointer to the PCI data structure is within the size of the image. | |
| // Then check it for valid signature. | |
| // | |
| if ((RomHdr->PcirOffset > FileSize) || (RomHdr->PcirOffset == 0)) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Invalid PCI data structure offset."); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Check the header is conform to PCI2.3 or PCI3.0 | |
| // | |
| if (mOptions.Pci23 == 1) { | |
| PciDs23 = (PCI_DATA_STRUCTURE *) (Buffer + RomHdr->PcirOffset); | |
| if (PciDs23->Signature != PCI_DATA_STRUCTURE_SIGNATURE) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "PCI data structure has an invalid signature."); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| } else { | |
| // | |
| // Default setting is PCI3.0 header | |
| // | |
| PciDs30 = (PCI_3_0_DATA_STRUCTURE *)(Buffer + RomHdr->PcirOffset); | |
| if (PciDs30->Signature != PCI_DATA_STRUCTURE_SIGNATURE) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "PCI data structure has an invalid signature."); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| } | |
| // | |
| // ReSet Option Rom size | |
| // | |
| if (mOptions.Pci23 == 1) { | |
| PciDs23->ImageLength = (UINT16) (TotalSize / 512); | |
| CodeType = PciDs23->CodeType; | |
| } else { | |
| PciDs30->ImageLength = (UINT16) (TotalSize / 512); | |
| CodeType = PciDs30->CodeType; | |
| } | |
| // | |
| // If this is the last image, then set the LAST bit unless requested not | |
| // to via the command-line -n argument. Otherwise, make sure you clear it. | |
| // | |
| if ((InFile->Next == NULL) && (mOptions.NoLast == 0)) { | |
| if (mOptions.Pci23 == 1) { | |
| PciDs23->Indicator = INDICATOR_LAST; | |
| } else { | |
| PciDs30->Indicator = INDICATOR_LAST; | |
| } | |
| } else { | |
| if (mOptions.Pci23 == 1) { | |
| PciDs23->Indicator = 0; | |
| } else { | |
| PciDs30->Indicator = 0; | |
| } | |
| } | |
| if (CodeType != PCI_CODE_TYPE_EFI_IMAGE) { | |
| ByteCheckSum = 0; | |
| for (Index = 0; Index < FileSize - 1; Index++) { | |
| ByteCheckSum = (UINT8) (ByteCheckSum + Buffer[Index]); | |
| } | |
| Buffer[FileSize - 1] = (UINT8) ((~ByteCheckSum) + 1); | |
| if (mOptions.Verbose) { | |
| VerboseMsg(" Checksum = %02x\n\n", Buffer[FileSize - 1]); | |
| } | |
| } | |
| // | |
| // Now copy the input file contents out to the output file | |
| // | |
| if (fwrite (Buffer, FileSize, 1, OutFptr) != 1) { | |
| Error (NULL, 0, 0005, "Failed to write all file bytes to output file.", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| TotalSize -= FileSize; | |
| // | |
| // Pad the rest of the image to make it a multiple of 512 bytes | |
| // | |
| while (TotalSize > 0) { | |
| putc (~0, OutFptr); | |
| TotalSize--; | |
| } | |
| BailOut: | |
| if (InFptr != NULL) { | |
| fclose (InFptr); | |
| } | |
| if (Buffer != NULL) { | |
| free (Buffer); | |
| } | |
| // | |
| // Print the file name if errors occurred | |
| // | |
| if (Status != STATUS_SUCCESS) { | |
| Error (NULL, 0, 0003, "Error", "Error parsing file: %s", InFile->FileName); | |
| } | |
| return Status; | |
| } | |
| static | |
| int | |
| ProcessEfiFile ( | |
| FILE *OutFptr, | |
| FILE_LIST *InFile, | |
| UINT16 VendId, | |
| UINT16 DevId, | |
| UINT32 *Size | |
| ) | |
| /*++ | |
| Routine Description: | |
| Process a PE32 EFI file. | |
| Arguments: | |
| OutFptr - file pointer to output binary ROM image file we're creating | |
| InFile - structure contains information on the PE32 file to process | |
| VendId - vendor ID as required in the option ROM header | |
| DevId - device ID as required in the option ROM header | |
| Size - pointer to where to return the size added to the output file | |
| Returns: | |
| 0 - successful | |
| --*/ | |
| { | |
| UINT32 Status; | |
| FILE *InFptr; | |
| EFI_PCI_EXPANSION_ROM_HEADER RomHdr; | |
| PCI_DATA_STRUCTURE PciDs23; | |
| PCI_3_0_DATA_STRUCTURE PciDs30; | |
| UINT32 FileSize; | |
| UINT32 CompressedFileSize; | |
| UINT8 *Buffer; | |
| UINT8 *CompressedBuffer; | |
| UINT8 *TempBufferPtr; | |
| UINT32 TotalSize; | |
| UINT32 HeaderSize; | |
| UINT16 MachineType; | |
| UINT16 SubSystem; | |
| UINT32 HeaderPadBytes; | |
| UINT32 PadBytesBeforeImage; | |
| UINT32 PadBytesAfterImage; | |
| UINT32 DevIdListSize; | |
| // | |
| // Try to open the input file | |
| // | |
| if ((InFptr = fopen (LongFilePath (InFile->FileName), "rb")) == NULL) { | |
| Error (NULL, 0, 0001, "Open file error", "Error opening file: %s", InFile->FileName); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Initialize our buffer pointers to null. | |
| // | |
| Buffer = NULL; | |
| CompressedBuffer = NULL; | |
| // | |
| // Double-check the file to make sure it's what we expect it to be | |
| // | |
| Status = CheckPE32File (InFptr, &MachineType, &SubSystem); | |
| if (Status != STATUS_SUCCESS) { | |
| goto BailOut; | |
| } | |
| // | |
| // Seek to the end of the input file and get the file size | |
| // | |
| fseek (InFptr, 0, SEEK_END); | |
| FileSize = ftell (InFptr); | |
| // | |
| // Get the size of the headers we're going to put in front of the image. The | |
| // EFI header must be aligned on a 4-byte boundary, so pad accordingly. | |
| // | |
| if (sizeof (RomHdr) & 0x03) { | |
| HeaderPadBytes = 4 - (sizeof (RomHdr) & 0x03); | |
| } else { | |
| HeaderPadBytes = 0; | |
| } | |
| // | |
| // For Pci3.0 to use the different data structure. | |
| // | |
| if (mOptions.Pci23 == 1) { | |
| HeaderSize = sizeof (PCI_DATA_STRUCTURE) + HeaderPadBytes + sizeof (EFI_PCI_EXPANSION_ROM_HEADER); | |
| } else { | |
| if (mOptions.DevIdCount > 1) { | |
| // | |
| // Write device ID list when more than one device ID is specified. | |
| // Leave space for list plus terminator. | |
| // | |
| DevIdListSize = (mOptions.DevIdCount + 1) * sizeof (UINT16); | |
| } else { | |
| DevIdListSize = 0; | |
| } | |
| HeaderSize = sizeof (PCI_3_0_DATA_STRUCTURE) + HeaderPadBytes + DevIdListSize + sizeof (EFI_PCI_EXPANSION_ROM_HEADER); | |
| } | |
| if (mOptions.Verbose) { | |
| VerboseMsg(" File size = 0x%X\n", (unsigned) FileSize); | |
| } | |
| // | |
| // Allocate memory for the entire file (in case we have to compress), then | |
| // seek back to the beginning of the file and read it into our buffer. | |
| // | |
| Buffer = (UINT8 *) malloc (FileSize); | |
| if (Buffer == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!"); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| fseek (InFptr, 0, SEEK_SET); | |
| if (fread (Buffer, FileSize, 1, InFptr) != 1) { | |
| Error (NULL, 0, 0004, "Error reading file", "File %s", InFile->FileName); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Now determine the size of the final output file. It's either the header size | |
| // plus the file's size, or the header size plus the compressed file size. | |
| // | |
| if ((InFile->FileFlags & FILE_FLAG_COMPRESS) != 0) { | |
| // | |
| // Allocate a buffer into which we can compress the image, compress it, | |
| // and use that size as the new size. | |
| // | |
| CompressedBuffer = (UINT8 *) malloc (FileSize); | |
| if (CompressedBuffer == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!"); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| CompressedFileSize = FileSize; | |
| Status = EfiCompress (Buffer, FileSize, CompressedBuffer, &CompressedFileSize); | |
| if (Status != STATUS_SUCCESS) { | |
| Error (NULL, 0, 0007, "Error compressing file!", NULL); | |
| goto BailOut; | |
| } | |
| // | |
| // Now compute the size, then swap buffer pointers. | |
| // | |
| if (mOptions.Verbose) { | |
| VerboseMsg(" Comp size = 0x%X\n", (unsigned) CompressedFileSize); | |
| } | |
| TotalSize = CompressedFileSize + HeaderSize; | |
| FileSize = CompressedFileSize; | |
| TempBufferPtr = Buffer; | |
| Buffer = CompressedBuffer; | |
| CompressedBuffer = TempBufferPtr; | |
| } else { | |
| TotalSize = FileSize + HeaderSize; | |
| } | |
| // | |
| // Total size must be an even multiple of 512 bytes | |
| // | |
| if (TotalSize & 0x1FF) { | |
| TotalSize = (TotalSize + 0x200) &~0x1ff; | |
| } | |
| // | |
| // Workaround: | |
| // If compressed, put the pad bytes after the image, | |
| // else put the pad bytes before the image. | |
| // | |
| if ((InFile->FileFlags & FILE_FLAG_COMPRESS) != 0) { | |
| PadBytesBeforeImage = 0; | |
| PadBytesAfterImage = TotalSize - (FileSize + HeaderSize); | |
| } else { | |
| PadBytesBeforeImage = TotalSize - (FileSize + HeaderSize); | |
| PadBytesAfterImage = 0; | |
| } | |
| // | |
| // Check size | |
| // | |
| if (TotalSize > MAX_OPTION_ROM_SIZE) { | |
| Error (NULL, 0, 2000, "Invalid", "Option ROM image %s size exceeds limit of 0x%X bytes.", InFile->FileName, MAX_OPTION_ROM_SIZE); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Return the size to the caller so they can keep track of the running total. | |
| // | |
| *Size = TotalSize; | |
| // | |
| // Now fill in the ROM header. These values come from chapter 18 of the | |
| // EFI 1.02 specification. | |
| // | |
| memset (&RomHdr, 0, sizeof (RomHdr)); | |
| RomHdr.Signature = PCI_EXPANSION_ROM_HEADER_SIGNATURE; | |
| RomHdr.InitializationSize = (UINT16) (TotalSize / 512); | |
| RomHdr.EfiSignature = EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE; | |
| RomHdr.EfiSubsystem = SubSystem; | |
| RomHdr.EfiMachineType = MachineType; | |
| RomHdr.EfiImageHeaderOffset = (UINT16) (HeaderSize + PadBytesBeforeImage); | |
| RomHdr.PcirOffset = (UINT16) (sizeof (RomHdr) + HeaderPadBytes); | |
| // | |
| // Set image as compressed or not | |
| // | |
| if (InFile->FileFlags & FILE_FLAG_COMPRESS) { | |
| RomHdr.CompressionType = EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED; | |
| } | |
| // | |
| // Fill in the PCI data structure | |
| // | |
| if (mOptions.Pci23 == 1) { | |
| memset (&PciDs23, 0, sizeof (PCI_DATA_STRUCTURE)); | |
| } else { | |
| memset (&PciDs30, 0, sizeof (PCI_3_0_DATA_STRUCTURE)); | |
| } | |
| if (mOptions.Pci23 == 1) { | |
| PciDs23.Signature = PCI_DATA_STRUCTURE_SIGNATURE; | |
| PciDs23.VendorId = VendId; | |
| PciDs23.DeviceId = DevId; | |
| PciDs23.Length = (UINT16) sizeof (PCI_DATA_STRUCTURE); | |
| PciDs23.Revision = 0; | |
| // | |
| // Class code and code revision from the command line (optional) | |
| // | |
| PciDs23.ClassCode[0] = (UINT8) InFile->ClassCode; | |
| PciDs23.ClassCode[1] = (UINT8) (InFile->ClassCode >> 8); | |
| PciDs23.ClassCode[2] = (UINT8) (InFile->ClassCode >> 16); | |
| PciDs23.ImageLength = RomHdr.InitializationSize; | |
| PciDs23.CodeRevision = InFile->CodeRevision; | |
| PciDs23.CodeType = PCI_CODE_TYPE_EFI_IMAGE; | |
| } else { | |
| PciDs30.Signature = PCI_DATA_STRUCTURE_SIGNATURE; | |
| PciDs30.VendorId = VendId; | |
| PciDs30.DeviceId = DevId; | |
| if (mOptions.DevIdCount > 1) { | |
| // | |
| // Place device list immediately after PCI structure | |
| // | |
| PciDs30.DeviceListOffset = (UINT16) sizeof (PCI_3_0_DATA_STRUCTURE); | |
| } else { | |
| PciDs30.DeviceListOffset = 0; | |
| } | |
| PciDs30.Length = (UINT16) sizeof (PCI_3_0_DATA_STRUCTURE); | |
| PciDs30.Revision = 0x3; | |
| // | |
| // Class code and code revision from the command line (optional) | |
| // | |
| PciDs30.ClassCode[0] = (UINT8) InFile->ClassCode; | |
| PciDs30.ClassCode[1] = (UINT8) (InFile->ClassCode >> 8); | |
| PciDs30.ClassCode[2] = (UINT8) (InFile->ClassCode >> 16); | |
| PciDs30.ImageLength = RomHdr.InitializationSize; | |
| PciDs30.CodeRevision = InFile->CodeRevision; | |
| PciDs30.CodeType = PCI_CODE_TYPE_EFI_IMAGE; | |
| PciDs30.MaxRuntimeImageLength = 0; // to be fixed | |
| PciDs30.ConfigUtilityCodeHeaderOffset = 0; // to be fixed | |
| PciDs30.DMTFCLPEntryPointOffset = 0; // to be fixed | |
| } | |
| // | |
| // If this is the last image, then set the LAST bit unless requested not | |
| // to via the command-line -n argument. | |
| // | |
| if ((InFile->Next == NULL) && (mOptions.NoLast == 0)) { | |
| if (mOptions.Pci23 == 1) { | |
| PciDs23.Indicator = INDICATOR_LAST; | |
| } else { | |
| PciDs30.Indicator = INDICATOR_LAST;} | |
| } else { | |
| if (mOptions.Pci23 == 1) { | |
| PciDs23.Indicator = 0; | |
| } else { | |
| PciDs30.Indicator = 0; | |
| } | |
| } | |
| // | |
| // Write the ROM header to the output file | |
| // | |
| if (fwrite (&RomHdr, sizeof (RomHdr), 1, OutFptr) != 1) { | |
| Error (NULL, 0, 0002, "Failed to write ROM header to output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Write pad bytes to align the PciDs | |
| // | |
| while (HeaderPadBytes > 0) { | |
| if (putc (0, OutFptr) == EOF) { | |
| Error (NULL, 0, 0002, "Failed to write ROM header pad bytes to output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| HeaderPadBytes--; | |
| } | |
| // | |
| // Write the PCI data structure header to the output file | |
| // | |
| if (mOptions.Pci23 == 1) { | |
| if (fwrite (&PciDs23, sizeof (PciDs23), 1, OutFptr) != 1) { | |
| Error (NULL, 0, 0002, "Failed to write PCI ROM header to output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| } else { | |
| if (fwrite (&PciDs30, sizeof (PciDs30), 1, OutFptr) != 1) { | |
| Error (NULL, 0, 0002, "Failed to write PCI ROM header to output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| } | |
| // | |
| // Write the Device ID list to the output file | |
| // | |
| if (mOptions.DevIdCount > 1) { | |
| if (fwrite (mOptions.DevIdList, sizeof (UINT16), mOptions.DevIdCount, OutFptr) != mOptions.DevIdCount) { | |
| Error (NULL, 0, 0002, "Failed to write PCI device list to output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Write two-byte terminating 0 at the end of the device list | |
| // | |
| if (putc (0, OutFptr) == EOF || putc (0, OutFptr) == EOF) { | |
| Error (NULL, 0, 0002, "Failed to write PCI device list to output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| } | |
| // | |
| // Pad head to make it a multiple of 512 bytes | |
| // | |
| while (PadBytesBeforeImage > 0) { | |
| if (putc (~0, OutFptr) == EOF) { | |
| Error (NULL, 0, 2000, "Failed to write trailing pad bytes output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| PadBytesBeforeImage--; | |
| } | |
| // | |
| // Now dump the input file's contents to the output file | |
| // | |
| if (fwrite (Buffer, FileSize, 1, OutFptr) != 1) { | |
| Error (NULL, 0, 0002, "Failed to write all file bytes to output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| // | |
| // Pad the rest of the image to make it a multiple of 512 bytes | |
| // | |
| while (PadBytesAfterImage > 0) { | |
| if (putc (~0, OutFptr) == EOF) { | |
| Error (NULL, 0, 2000, "Failed to write trailing pad bytes output file!", NULL); | |
| Status = STATUS_ERROR; | |
| goto BailOut; | |
| } | |
| PadBytesAfterImage--; | |
| } | |
| BailOut: | |
| if (InFptr != NULL) { | |
| fclose (InFptr); | |
| } | |
| // | |
| // Free up our buffers | |
| // | |
| if (Buffer != NULL) { | |
| free (Buffer); | |
| } | |
| if (CompressedBuffer != NULL) { | |
| free (CompressedBuffer); | |
| } | |
| // | |
| // Print the file name if errors occurred | |
| // | |
| if (Status != STATUS_SUCCESS) { | |
| Error (NULL, 0, 0003, "Error parsing", "Error parsing file: %s", InFile->FileName); | |
| } | |
| return Status; | |
| } | |
| static | |
| int | |
| CheckPE32File ( | |
| FILE *Fptr, | |
| UINT16 *MachineType, | |
| UINT16 *SubSystem | |
| ) | |
| /*++ | |
| Routine Description: | |
| Given a file pointer to a supposed PE32 image file, verify that it is indeed a | |
| PE32 image file, and then return the machine type in the supplied pointer. | |
| Arguments: | |
| Fptr File pointer to the already-opened PE32 file | |
| MachineType Location to stuff the machine type of the PE32 file. This is needed | |
| because the image may be Itanium-based, IA32, or EBC. | |
| Returns: | |
| 0 success | |
| non-zero otherwise | |
| --*/ | |
| { | |
| EFI_IMAGE_DOS_HEADER DosHeader; | |
| EFI_IMAGE_OPTIONAL_HEADER_UNION PeHdr; | |
| // | |
| // Position to the start of the file | |
| // | |
| fseek (Fptr, 0, SEEK_SET); | |
| // | |
| // Read the DOS header | |
| // | |
| if (fread (&DosHeader, sizeof (DosHeader), 1, Fptr) != 1) { | |
| Error (NULL, 0, 0004, "Failed to read the DOS stub from the input file!", NULL); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Check the magic number (0x5A4D) | |
| // | |
| if (DosHeader.e_magic != EFI_IMAGE_DOS_SIGNATURE) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Input file does not appear to be a PE32 image (magic number)!"); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Position into the file and check the PE signature | |
| // | |
| fseek (Fptr, (long) DosHeader.e_lfanew, SEEK_SET); | |
| // | |
| // Read PE headers | |
| // | |
| if (fread (&PeHdr, sizeof (PeHdr), 1, Fptr) != 1) { | |
| Error (NULL, 0, 0004, "Failed to read PE/COFF headers from input file!", NULL); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Check the PE signature in the header "PE\0\0" | |
| // | |
| if (PeHdr.Pe32.Signature != EFI_IMAGE_NT_SIGNATURE) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Input file does not appear to be a PE32 image (signature)!"); | |
| return STATUS_ERROR; | |
| } | |
| memcpy ((char *) MachineType, &PeHdr.Pe32.FileHeader.Machine, 2); | |
| if (PeHdr.Pe32.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { | |
| *SubSystem = PeHdr.Pe32.OptionalHeader.Subsystem; | |
| } else if (PeHdr.Pe32Plus.OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC) { | |
| *SubSystem = PeHdr.Pe32Plus.OptionalHeader.Subsystem; | |
| } else { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Unable to find subsystem type!"); | |
| return STATUS_ERROR; | |
| } | |
| if (mOptions.Verbose) { | |
| VerboseMsg(" Got subsystem = 0x%X from image\n", *SubSystem); | |
| } | |
| // | |
| // File was successfully identified as a PE32 | |
| // | |
| return STATUS_SUCCESS; | |
| } | |
| static | |
| int | |
| ParseCommandLine ( | |
| int Argc, | |
| char *Argv[], | |
| OPTIONS *Options | |
| ) | |
| /*++ | |
| Routine Description: | |
| Given the Argc/Argv program arguments, and a pointer to an options structure, | |
| parse the command-line options and check their validity. | |
| Arguments: | |
| Argc - standard C main() argument count | |
| Argv[] - standard C main() argument list | |
| Options - pointer to a structure to store the options in | |
| Returns: | |
| STATUS_SUCCESS success | |
| non-zero otherwise | |
| --*/ | |
| { | |
| FILE_LIST *FileList; | |
| FILE_LIST *PrevFileList; | |
| UINT32 FileFlags; | |
| UINT32 ClassCode; | |
| UINT32 CodeRevision; | |
| EFI_STATUS Status; | |
| INTN ReturnStatus; | |
| BOOLEAN EfiRomFlag; | |
| UINT64 TempValue; | |
| char *OptionName; | |
| UINT16 *DevIdList; | |
| ReturnStatus = 0; | |
| FileFlags = 0; | |
| EfiRomFlag = FALSE; | |
| // | |
| // Clear out the options | |
| // | |
| memset ((char *) Options, 0, sizeof (OPTIONS)); | |
| // | |
| // To avoid compile warnings | |
| // | |
| FileList = PrevFileList = NULL; | |
| Options->DevIdList = NULL; | |
| Options->DevIdCount = 0; | |
| ClassCode = 0; | |
| CodeRevision = 0; | |
| // | |
| // Skip over the program name | |
| // | |
| Argc--; | |
| Argv++; | |
| // | |
| // If no arguments, assume they want usage info | |
| // | |
| if (Argc == 0) { | |
| Usage (); | |
| return STATUS_ERROR; | |
| } | |
| if ((stricmp(Argv[0], "-h") == 0) || (stricmp(Argv[0], "--help") == 0)) { | |
| Usage(); | |
| return STATUS_ERROR; | |
| } | |
| if ((stricmp(Argv[0], "--version") == 0)) { | |
| Version(); | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // Process until no more arguments | |
| // | |
| while (Argc > 0) { | |
| if (Argv[0][0] == '-') { | |
| // | |
| // Vendor ID specified with -f | |
| // | |
| if (stricmp (Argv[0], "-f") == 0) { | |
| // | |
| // Make sure there's another parameter | |
| // | |
| Status = AsciiStringToUint64(Argv[1], FALSE, &TempValue); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 2000, "Invalid option value", "%s = %s", Argv[0], Argv[1]); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| if (TempValue >= 0x10000) { | |
| Error (NULL, 0, 2000, "Invalid option value", "Vendor Id %s out of range!", Argv[1]); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| Options->VendId = (UINT16) TempValue; | |
| Options->VendIdValid = 1; | |
| Argv++; | |
| Argc--; | |
| } else if (stricmp (Argv[0], "-i") == 0) { | |
| OptionName = Argv[0]; | |
| // | |
| // Device IDs specified with -i | |
| // Make sure there's at least one more parameter | |
| // | |
| if (Argc == 1) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Missing Device Id with %s option!", OptionName); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| // | |
| // Process until another dash-argument parameter or the end of the list | |
| // | |
| while (Argc > 1 && Argv[1][0] != '-') { | |
| Status = AsciiStringToUint64(Argv[1], FALSE, &TempValue); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 2000, "Invalid option value", "%s = %s", OptionName, Argv[1]); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| // | |
| // Don't allow device IDs greater than 16 bits | |
| // Don't allow 0, since it is used as a list terminator | |
| // | |
| if (TempValue >= 0x10000 || TempValue == 0) { | |
| Error (NULL, 0, 2000, "Invalid option value", "Device Id %s out of range!", Argv[1]); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| DevIdList = (UINT16*) realloc (Options->DevIdList, (Options->DevIdCount + 1) * sizeof (UINT16)); | |
| if (DevIdList == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!", NULL); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| Options->DevIdList = DevIdList; | |
| Options->DevIdList[Options->DevIdCount++] = (UINT16) TempValue; | |
| Argv++; | |
| Argc--; | |
| } | |
| } else if ((stricmp (Argv[0], "-o") == 0) || (stricmp (Argv[0], "--output") == 0)) { | |
| // | |
| // Output filename specified with -o | |
| // Make sure there's another parameter | |
| // | |
| if (Argv[1] == NULL || Argv[1][0] == '-') { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Missing output file name with %s option!", Argv[0]); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| if (strlen (Argv[1]) > MAX_PATH - 1) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Output file name %s is too long!", Argv[1]); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| strncpy (Options->OutFileName, Argv[1], MAX_PATH - 1); | |
| Options->OutFileName[MAX_PATH - 1] = 0; | |
| Argv++; | |
| Argc--; | |
| } else if ((stricmp (Argv[0], "-h") == 0) || (stricmp (Argv[0], "--help") == 0)) { | |
| // | |
| // Help option | |
| // | |
| Usage (); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } else if (stricmp (Argv[0], "-b") == 0) { | |
| // | |
| // Specify binary files with -b | |
| // | |
| FileFlags = FILE_FLAG_BINARY; | |
| } else if ((stricmp (Argv[0], "-e") == 0) || (stricmp (Argv[0], "-ec") == 0)) { | |
| // | |
| // Specify EFI files with -e. Specify EFI-compressed with -c. | |
| // | |
| FileFlags = FILE_FLAG_EFI; | |
| if ((Argv[0][2] == 'c') || (Argv[0][2] == 'C')) { | |
| FileFlags |= FILE_FLAG_COMPRESS; | |
| } | |
| // | |
| // Specify not to set the LAST bit in the last file with -n | |
| // | |
| } else if (stricmp (Argv[0], "-n") == 0) { | |
| Options->NoLast = 1; | |
| } else if (((stricmp (Argv[0], "-v") == 0)) || ((stricmp (Argv[0], "--verbose") == 0))) { | |
| // | |
| // -v for verbose | |
| // | |
| Options->Verbose = 1; | |
| } else if (stricmp (Argv[0], "--debug") == 0) { | |
| Status = AsciiStringToUint64(Argv[1], FALSE, &DebugLevel); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 2000, "Invalid option value", "%s = %s", Argv[0], Argv[1]); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| if (DebugLevel > 9) { | |
| Error (NULL, 0, 2000, "Invalid option value", "Debug Level range is 0-9, current input level is %llu", DebugLevel); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| if (DebugLevel>=5 && DebugLevel<=9) { | |
| Options->Debug = TRUE; | |
| } else { | |
| Options->Debug = FALSE; | |
| } | |
| Argv++; | |
| Argc--; | |
| } else if ((stricmp (Argv[0], "--quiet") == 0) || (stricmp (Argv[0], "-q") == 0)) { | |
| Options->Quiet = TRUE; | |
| } else if ((stricmp (Argv[0], "--dump") == 0) || (stricmp (Argv[0], "-d") == 0)) { | |
| // | |
| // -dump for dumping a ROM image. In this case, say that the device id | |
| // and vendor id are valid so we don't have to specify bogus ones on the | |
| // command line. | |
| // | |
| Options->DumpOption = 1; | |
| Options->VendIdValid = 1; | |
| Options->DevIdCount = 1; | |
| FileFlags = FILE_FLAG_BINARY; | |
| } else if ((stricmp (Argv[0], "-l") == 0) || (stricmp (Argv[0], "--class-code") == 0)) { | |
| // | |
| // Class code value for the next file in the list. | |
| // Make sure there's another parameter | |
| // | |
| Status = AsciiStringToUint64(Argv[1], FALSE, &TempValue); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 2000, "Invalid option value", "%s = %s", Argv[0], Argv[1]); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| ClassCode = (UINT32) TempValue; | |
| if (ClassCode & 0xFF000000) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Class code %s out of range!", Argv[1]); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| if (FileList != NULL && FileList->ClassCode == 0) { | |
| FileList->ClassCode = ClassCode; | |
| } | |
| Argv++; | |
| Argc--; | |
| } else if ((stricmp (Argv[0], "-r") == 0) || (stricmp (Argv[0], "--Revision") == 0)) { | |
| // | |
| // Code revision in the PCI data structure. The value is for the next | |
| // file in the list. | |
| // Make sure there's another parameter | |
| // | |
| Status = AsciiStringToUint64(Argv[1], FALSE, &TempValue); | |
| if (EFI_ERROR (Status)) { | |
| Error (NULL, 0, 2000, "Invalid option value", "%s = %s", Argv[0], Argv[1]); | |
| ReturnStatus = 1; | |
| goto Done; | |
| } | |
| CodeRevision = (UINT32) TempValue; | |
| if (CodeRevision & 0xFFFF0000) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Code revision %s out of range!", Argv[1]); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| if (FileList != NULL && FileList->CodeRevision == 0) { | |
| FileList->CodeRevision = (UINT16) CodeRevision; | |
| } | |
| Argv++; | |
| Argc--; | |
| } else if ((stricmp (Argv[0], "-p") == 0) || (stricmp (Argv[0], "--pci23") == 0)) { | |
| // | |
| // Default layout meets PCI 3.0 specifications, specifying this flag will for a PCI 2.3 layout. | |
| // | |
| mOptions.Pci23 = 1; | |
| } else { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Invalid option specified: %s", Argv[0]); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| } else { | |
| // | |
| // Not a slash-option argument. Must be a file name. Make sure they've specified | |
| // -e or -b already. | |
| // | |
| if ((FileFlags & (FILE_FLAG_BINARY | FILE_FLAG_EFI)) == 0) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Missing -e or -b with input file %s!", Argv[0]); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| // | |
| // Check Efi Option RomImage | |
| // | |
| if ((FileFlags & FILE_FLAG_EFI) == FILE_FLAG_EFI) { | |
| EfiRomFlag = TRUE; | |
| } | |
| // | |
| // Create a new file structure | |
| // | |
| FileList = (FILE_LIST *) malloc (sizeof (FILE_LIST)); | |
| if (FileList == NULL) { | |
| Error (NULL, 0, 4001, "Resource", "memory cannot be allocated!", NULL); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| // | |
| // set flag and class code for this image. | |
| // | |
| memset ((char *) FileList, 0, sizeof (FILE_LIST)); | |
| FileList->FileName = Argv[0]; | |
| FileList->FileFlags = FileFlags; | |
| FileList->ClassCode = ClassCode; | |
| FileList->CodeRevision = (UINT16) CodeRevision; | |
| ClassCode = 0; | |
| CodeRevision = 0; | |
| if (Options->FileList == NULL) { | |
| Options->FileList = FileList; | |
| } else { | |
| if (PrevFileList == NULL) { | |
| PrevFileList = FileList; | |
| } else { | |
| PrevFileList->Next = FileList; | |
| } | |
| } | |
| PrevFileList = FileList; | |
| } | |
| // | |
| // Next argument | |
| // | |
| Argv++; | |
| Argc--; | |
| } | |
| // | |
| // Must have specified some files | |
| // | |
| if (Options->FileList == NULL) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "Missing input file name!"); | |
| // | |
| // No memory allocation, return directly. | |
| // | |
| return STATUS_ERROR; | |
| } | |
| // | |
| // For EFI OptionRom image, Make sure a device ID and vendor ID are both specified. | |
| // | |
| if (EfiRomFlag) { | |
| if (!Options->VendIdValid) { | |
| Error (NULL, 0, 2000, "Missing Vendor ID in command line", NULL); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| if (!Options->DevIdCount) { | |
| Error (NULL, 0, 2000, "Missing Device ID in command line", NULL); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| } | |
| if (Options->DevIdCount > 1 && Options->Pci23) { | |
| Error (NULL, 0, 2000, "Invalid parameter", "PCI 3.0 is required when specifying multiple Device IDs"); | |
| ReturnStatus = STATUS_ERROR; | |
| goto Done; | |
| } | |
| Done: | |
| if (ReturnStatus != 0) { | |
| while (Options->FileList != NULL) { | |
| FileList = Options->FileList->Next; | |
| free (Options->FileList); | |
| Options->FileList = FileList; | |
| } | |
| } | |
| return ReturnStatus; | |
| } | |
| static | |
| void | |
| Version ( | |
| VOID | |
| ) | |
| /*++ | |
| Routine Description: | |
| Print version information for this utility. | |
| Arguments: | |
| None. | |
| Returns: | |
| Nothing. | |
| --*/ | |
| { | |
| 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 usage information for this utility. | |
| Arguments: | |
| None. | |
| Returns: | |
| Nothing. | |
| --*/ | |
| { | |
| // | |
| // Summary usage | |
| // | |
| fprintf (stdout, "Usage: %s -f VendorId -i DeviceId [options] [file name<s>] \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, --output FileName\n\ | |
| File will be created to store the output content.\n"); | |
| fprintf (stdout, " -e EfiFileName\n\ | |
| EFI PE32 image files.\n"); | |
| fprintf (stdout, " -ec EfiFileName\n\ | |
| EFI PE32 image files and will be compressed.\n"); | |
| fprintf (stdout, " -b BinFileName\n\ | |
| Legacy binary files.\n"); | |
| fprintf (stdout, " -l ClassCode\n\ | |
| Hex ClassCode in the PCI data structure header.\n"); | |
| fprintf (stdout, " -r Rev Hex Revision in the PCI data structure header.\n"); | |
| fprintf (stdout, " -n Not to automatically set the LAST bit in the last file.\n"); | |
| fprintf (stdout, " -f VendorId\n\ | |
| Hex PCI Vendor ID for the device OpROM, must be specified\n"); | |
| fprintf (stdout, " -i DeviceId\n\ | |
| One or more hex PCI Device IDs for the device OpROM, must be specified\n"); | |
| fprintf (stdout, " -p, --pci23\n\ | |
| Default layout meets PCI 3.0 specifications\n\ | |
| specifying this flag will for a PCI 2.3 layout.\n"); | |
| fprintf (stdout, " -d, --dump\n\ | |
| Dump the headers of an existing option ROM image.\n"); | |
| fprintf (stdout, " -v, --verbose\n\ | |
| Turn on verbose output with informational messages.\n"); | |
| fprintf (stdout, " --version Show program's version number and exit.\n"); | |
| fprintf (stdout, " -h, --help\n\ | |
| Show this help message and exit.\n"); | |
| fprintf (stdout, " -q, --quiet\n\ | |
| Disable all messages except FATAL ERRORS.\n"); | |
| fprintf (stdout, " --debug [#,0-9]\n\ | |
| Enable debug messages at level #.\n"); | |
| } | |
| static | |
| void | |
| DumpImage ( | |
| FILE_LIST *InFile | |
| ) | |
| /*++ | |
| Routine Description: | |
| Dump the headers of an existing option ROM image | |
| Arguments: | |
| InFile - the file name of an existing option ROM image | |
| Returns: | |
| none | |
| --*/ | |
| { | |
| PCI_EXPANSION_ROM_HEADER PciRomHdr; | |
| FILE *InFptr; | |
| UINT32 ImageStart; | |
| UINT32 ImageCount; | |
| EFI_PCI_EXPANSION_ROM_HEADER EfiRomHdr; | |
| PCI_DATA_STRUCTURE PciDs23; | |
| PCI_3_0_DATA_STRUCTURE PciDs30; | |
| UINT16 DevId; | |
| // | |
| // Open the input file | |
| // | |
| if ((InFptr = fopen (LongFilePath (InFile->FileName), "rb")) == NULL) { | |
| Error (NULL, 0, 0001, "Error opening file", InFile->FileName); | |
| return ; | |
| } | |
| // | |
| // Go through the image and dump the header stuff for each | |
| // | |
| ImageCount = 0; | |
| for (;;) { | |
| // | |
| // Save our position in the file, since offsets in the headers | |
| // are relative to the particular image. | |
| // | |
| ImageStart = ftell (InFptr); | |
| ImageCount++; | |
| // | |
| // Read the option ROM header. Have to assume a raw binary image for now. | |
| // | |
| if (fread (&PciRomHdr, sizeof (PciRomHdr), 1, InFptr) != 1) { | |
| Error (NULL, 0, 3001, "Not supported", "Failed to read PCI ROM header from file!"); | |
| goto BailOut; | |
| } | |
| // | |
| // Dump the contents of the header | |
| // | |
| fprintf (stdout, "Image %u -- Offset 0x%X\n", (unsigned) ImageCount, (unsigned) ImageStart); | |
| fprintf (stdout, " ROM header contents\n"); | |
| fprintf (stdout, " Signature 0x%04X\n", PciRomHdr.Signature); | |
| fprintf (stdout, " PCIR offset 0x%04X\n", PciRomHdr.PcirOffset); | |
| // | |
| // Find PCI data structure | |
| // | |
| if (fseek (InFptr, ImageStart + PciRomHdr.PcirOffset, SEEK_SET)) { | |
| Error (NULL, 0, 3001, "Not supported", "Failed to seek to PCI data structure!"); | |
| goto BailOut; | |
| } | |
| // | |
| // Read and dump the PCI data structure | |
| // | |
| memset (&PciDs23, 0, sizeof (PciDs23)); | |
| memset (&PciDs30, 0, sizeof (PciDs30)); | |
| if (mOptions.Pci23 == 1) { | |
| if (fread (&PciDs23, sizeof (PciDs23), 1, InFptr) != 1) { | |
| Error (NULL, 0, 3001, "Not supported", "Failed to read PCI data structure from file %s!", InFile->FileName); | |
| goto BailOut; | |
| } | |
| } else { | |
| if (fread (&PciDs30, sizeof (PciDs30), 1, InFptr) != 1) { | |
| Error (NULL, 0, 3001, "Not supported", "Failed to read PCI data structure from file %s!", InFile->FileName); | |
| goto BailOut; | |
| } | |
| } | |
| if (mOptions.Verbose) { | |
| VerboseMsg("Read PCI data structure from file %s", InFile->FileName); | |
| } | |
| //fprintf (stdout, " PCI Data Structure\n"); | |
| if (mOptions.Pci23 == 1) { | |
| fprintf ( | |
| stdout, | |
| " Signature %c%c%c%c\n", | |
| (char) PciDs23.Signature, | |
| (char) (PciDs23.Signature >> 8), | |
| (char) (PciDs23.Signature >> 16), | |
| (char) (PciDs23.Signature >> 24) | |
| ); | |
| fprintf (stdout, " Vendor ID 0x%04X\n", PciDs23.VendorId); | |
| fprintf (stdout, " Device ID 0x%04X\n", PciDs23.DeviceId); | |
| fprintf (stdout, " Length 0x%04X\n", PciDs23.Length); | |
| fprintf (stdout, " Revision 0x%04X\n", PciDs23.Revision); | |
| fprintf ( | |
| stdout, | |
| " Class Code 0x%06X\n", | |
| (unsigned) (PciDs23.ClassCode[0] | (PciDs23.ClassCode[1] << 8) | (PciDs23.ClassCode[2] << 16)) | |
| ); | |
| fprintf (stdout, " Image size 0x%X\n", (unsigned) PciDs23.ImageLength * 512); | |
| fprintf (stdout, " Code revision: 0x%04X\n", PciDs23.CodeRevision); | |
| fprintf (stdout, " Indicator 0x%02X", PciDs23.Indicator); | |
| } else { | |
| fprintf ( | |
| stdout, | |
| " Signature %c%c%c%c\n", | |
| (char) PciDs30.Signature, | |
| (char) (PciDs30.Signature >> 8), | |
| (char) (PciDs30.Signature >> 16), | |
| (char) (PciDs30.Signature >> 24) | |
| ); | |
| fprintf (stdout, " Vendor ID 0x%04X\n", PciDs30.VendorId); | |
| fprintf (stdout, " Device ID 0x%04X\n", PciDs30.DeviceId); | |
| fprintf (stdout, " Length 0x%04X\n", PciDs30.Length); | |
| fprintf (stdout, " Revision 0x%04X\n", PciDs30.Revision); | |
| fprintf (stdout, " DeviceListOffset 0x%02X\n", PciDs30.DeviceListOffset); | |
| if (PciDs30.DeviceListOffset) { | |
| // | |
| // Print device ID list | |
| // | |
| fprintf (stdout, " Device list contents\n"); | |
| if (fseek (InFptr, ImageStart + PciRomHdr.PcirOffset + PciDs30.DeviceListOffset, SEEK_SET)) { | |
| Error (NULL, 0, 3001, "Not supported", "Failed to seek to PCI device ID list!"); | |
| goto BailOut; | |
| } | |
| // | |
| // Loop until terminating 0 | |
| // | |
| do { | |
| if (fread (&DevId, sizeof (DevId), 1, InFptr) != 1) { | |
| Error (NULL, 0, 3001, "Not supported", "Failed to read PCI device ID list from file %s!", InFile->FileName); | |
| goto BailOut; | |
| } | |
| if (DevId) { | |
| fprintf (stdout, " 0x%04X\n", DevId); | |
| } | |
| } while (DevId); | |
| } | |
| fprintf ( | |
| stdout, | |
| " Class Code 0x%06X\n", | |
| (unsigned) (PciDs30.ClassCode[0] | (PciDs30.ClassCode[1] << 8) | (PciDs30.ClassCode[2] << 16)) | |
| ); | |
| fprintf (stdout, " Image size 0x%X\n", (unsigned) PciDs30.ImageLength * 512); | |
| fprintf (stdout, " Code revision: 0x%04X\n", PciDs30.CodeRevision); | |
| fprintf (stdout, " MaxRuntimeImageLength 0x%02X\n", PciDs30.MaxRuntimeImageLength); | |
| fprintf (stdout, " ConfigUtilityCodeHeaderOffset 0x%02X\n", PciDs30.ConfigUtilityCodeHeaderOffset); | |
| fprintf (stdout, " DMTFCLPEntryPointOffset 0x%02X\n", PciDs30.DMTFCLPEntryPointOffset); | |
| fprintf (stdout, " Indicator 0x%02X", PciDs30.Indicator); | |
| } | |
| // | |
| // Print the indicator, used to flag the last image | |
| // | |
| if (PciDs23.Indicator == INDICATOR_LAST || PciDs30.Indicator == INDICATOR_LAST) { | |
| fprintf (stdout, " (last image)\n"); | |
| } else { | |
| fprintf (stdout, "\n"); | |
| } | |
| // | |
| // Print the code type. If EFI code, then we can provide more info. | |
| // | |
| if (mOptions.Pci23 == 1) { | |
| fprintf (stdout, " Code type 0x%02X", PciDs23.CodeType); | |
| } else { | |
| fprintf (stdout, " Code type 0x%02X", PciDs30.CodeType); | |
| } | |
| if (PciDs23.CodeType == PCI_CODE_TYPE_EFI_IMAGE || PciDs30.CodeType == PCI_CODE_TYPE_EFI_IMAGE) { | |
| fprintf (stdout, " (EFI image)\n"); | |
| // | |
| // Re-read the header as an EFI ROM header, then dump more info | |
| // | |
| fprintf (stdout, " EFI ROM header contents\n"); | |
| if (fseek (InFptr, ImageStart, SEEK_SET)) { | |
| Error (NULL, 0, 5001, "Failed to re-seek to ROM header structure!", NULL); | |
| goto BailOut; | |
| } | |
| if (fread (&EfiRomHdr, sizeof (EfiRomHdr), 1, InFptr) != 1) { | |
| Error (NULL, 0, 5001, "Failed to read EFI PCI ROM header from file!", NULL); | |
| goto BailOut; | |
| } | |
| // | |
| // Now dump more info | |
| // | |
| fprintf (stdout, " EFI Signature 0x%04X\n", (unsigned) EfiRomHdr.EfiSignature); | |
| fprintf ( | |
| stdout, | |
| " Compression Type 0x%04X ", | |
| EfiRomHdr.CompressionType | |
| ); | |
| if (EfiRomHdr.CompressionType == EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED) { | |
| fprintf (stdout, "(compressed)\n"); | |
| } else { | |
| fprintf (stdout, "(not compressed)\n"); | |
| } | |
| fprintf ( | |
| stdout, | |
| " Machine type 0x%04X (%s)\n", | |
| EfiRomHdr.EfiMachineType, | |
| GetMachineTypeStr (EfiRomHdr.EfiMachineType) | |
| ); | |
| fprintf ( | |
| stdout, | |
| " Subsystem 0x%04X (%s)\n", | |
| EfiRomHdr.EfiSubsystem, | |
| GetSubsystemTypeStr (EfiRomHdr.EfiSubsystem) | |
| ); | |
| fprintf ( | |
| stdout, | |
| " EFI image offset 0x%04X (@0x%X)\n", | |
| EfiRomHdr.EfiImageHeaderOffset, | |
| EfiRomHdr.EfiImageHeaderOffset + (unsigned) ImageStart | |
| ); | |
| } else { | |
| // | |
| // Not an EFI image | |
| // | |
| fprintf (stdout, "\n"); | |
| } | |
| // | |
| // If code type is EFI image, then dump it as well? | |
| // | |
| // if (PciDs.CodeType == PCI_CODE_TYPE_EFI_IMAGE) { | |
| // } | |
| // | |
| // If last image, then we're done | |
| // | |
| if (PciDs23.Indicator == INDICATOR_LAST || PciDs30.Indicator == INDICATOR_LAST) { | |
| goto BailOut; | |
| } | |
| // | |
| // Seek to the start of the next image | |
| // | |
| if (mOptions.Pci23 == 1) { | |
| if (fseek (InFptr, ImageStart + (PciDs23.ImageLength * 512), SEEK_SET)) { | |
| Error (NULL, 0, 3001, "Not supported", "Failed to seek to next image!"); | |
| goto BailOut; | |
| } | |
| } else { | |
| if (fseek (InFptr, ImageStart + (PciDs30.ImageLength * 512), SEEK_SET)) { | |
| Error (NULL, 0, 3001, "Not supported", "Failed to seek to next image!"); | |
| goto BailOut; | |
| } | |
| } | |
| } | |
| BailOut: | |
| fclose (InFptr); | |
| } | |
| char * | |
| GetMachineTypeStr ( | |
| UINT16 MachineType | |
| ) | |
| /*++ | |
| Routine Description: | |
| GC_TODO: Add function description | |
| Arguments: | |
| MachineType - GC_TODO: add argument description | |
| Returns: | |
| GC_TODO: add return values | |
| --*/ | |
| { | |
| int Index; | |
| for (Index = 0; mMachineTypes[Index].Name != NULL; Index++) { | |
| if (mMachineTypes[Index].Value == MachineType) { | |
| return mMachineTypes[Index].Name; | |
| } | |
| } | |
| return "unknown"; | |
| } | |
| static | |
| char * | |
| GetSubsystemTypeStr ( | |
| UINT16 SubsystemType | |
| ) | |
| /*++ | |
| Routine Description: | |
| GC_TODO: Add function description | |
| Arguments: | |
| SubsystemType - GC_TODO: add argument description | |
| Returns: | |
| GC_TODO: add return values | |
| --*/ | |
| { | |
| int Index; | |
| for (Index = 0; mSubsystemTypes[Index].Name != NULL; Index++) { | |
| if (mSubsystemTypes[Index].Value == SubsystemType) { | |
| return mSubsystemTypes[Index].Name; | |
| } | |
| } | |
| return "unknown"; | |
| } |