mirror of https://github.com/acidanthera/audk.git
1546 lines
40 KiB
C
1546 lines
40 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 2007, Intel Corporation
|
||
|
All rights reserved. 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.
|
||
|
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
EfiRom.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Utility program to create an EFI option ROM image from binary and
|
||
|
EFI PE32 files.
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
//
|
||
|
// Includes for EFI 1.1 build
|
||
|
//
|
||
|
// #include "Tiano.h" // required defines for Compress.h
|
||
|
// #include "EfiImage.h" // for PE32 structure definitions
|
||
|
// #include "Compress.h" // for compression function
|
||
|
// Includes for Tiano build
|
||
|
//
|
||
|
#include "TianoCommon.h"
|
||
|
#include "EfiImage.h" // for PE32 structure definitions
|
||
|
#include "Compress.h"
|
||
|
|
||
|
//
|
||
|
// END include differences
|
||
|
//
|
||
|
#include "Pci22.h" // for option ROM header structures
|
||
|
//
|
||
|
// Version of this utility
|
||
|
//
|
||
|
#define UTILITY_VERSION "v2.5"
|
||
|
|
||
|
//
|
||
|
// Define some status return values
|
||
|
//
|
||
|
#define STATUS_SUCCESS 0
|
||
|
#define STATUS_WARNING 1
|
||
|
#define STATUS_ERROR 2
|
||
|
|
||
|
//
|
||
|
// Define the max length of a filename
|
||
|
//
|
||
|
#define MAX_PATH 200
|
||
|
|
||
|
#define DEFAULT_OUTPUT_EXTENSION ".rom"
|
||
|
|
||
|
//
|
||
|
// Max size for an option ROM image
|
||
|
//
|
||
|
#define MAX_OPTION_ROM_SIZE (1024 * 1024 * 16) // 16MB
|
||
|
//
|
||
|
// Values for the indicator field in the PCI data structure
|
||
|
//
|
||
|
#define INDICATOR_LAST 0x80 // last file in series of files
|
||
|
//
|
||
|
// Masks for the FILE_LIST.FileFlags field
|
||
|
//
|
||
|
#define FILE_FLAG_BINARY 0x01
|
||
|
#define FILE_FLAG_EFI 0x02
|
||
|
#define FILE_FLAG_COMPRESS 0x04
|
||
|
|
||
|
//
|
||
|
// Use this linked list structure to keep track of all the filenames
|
||
|
// specified on the command line.
|
||
|
//
|
||
|
typedef struct _FILE_LIST {
|
||
|
struct _FILE_LIST *Next;
|
||
|
INT8 *FileName;
|
||
|
UINT32 FileFlags;
|
||
|
UINT32 ClassCode;
|
||
|
UINT16 CodeRevision;
|
||
|
} FILE_LIST;
|
||
|
|
||
|
//
|
||
|
// Use this to track our command-line options
|
||
|
//
|
||
|
typedef struct {
|
||
|
INT8 OutFileName[MAX_PATH];
|
||
|
INT8 NoLast;
|
||
|
INT8 Verbose;
|
||
|
INT8 DumpOption;
|
||
|
UINT8 DevIdValid;
|
||
|
UINT8 VendIdValid;
|
||
|
UINT16 VendId;
|
||
|
UINT16 DevId;
|
||
|
FILE_LIST *FileList;
|
||
|
} OPTIONS;
|
||
|
|
||
|
//
|
||
|
// Make a global structure to keep track of command-line options
|
||
|
//
|
||
|
static OPTIONS mOptions;
|
||
|
|
||
|
//
|
||
|
// Use these to convert from machine type value to a named type
|
||
|
//
|
||
|
typedef struct {
|
||
|
UINT16 Value;
|
||
|
char *Name;
|
||
|
} STRING_LOOKUP;
|
||
|
|
||
|
static STRING_LOOKUP mMachineTypes[] = {
|
||
|
EFI_IMAGE_MACHINE_IA32,
|
||
|
"IA32",
|
||
|
EFI_IMAGE_MACHINE_IA64,
|
||
|
"IA64",
|
||
|
EFI_IMAGE_MACHINE_EBC,
|
||
|
"EBC",
|
||
|
0,
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
static STRING_LOOKUP mSubsystemTypes[] = {
|
||
|
EFI_IMAGE_SUBSYSTEM_EFI_APPLICATION,
|
||
|
"EFI application",
|
||
|
EFI_IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER,
|
||
|
"EFI boot service driver",
|
||
|
EFI_IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER,
|
||
|
"EFI runtime driver",
|
||
|
0,
|
||
|
NULL
|
||
|
};
|
||
|
//
|
||
|
// Function prototypes
|
||
|
//
|
||
|
static
|
||
|
void
|
||
|
Usage (
|
||
|
VOID
|
||
|
);
|
||
|
|
||
|
static
|
||
|
int
|
||
|
ParseCommandLine (
|
||
|
int Argc,
|
||
|
char *Argv[],
|
||
|
OPTIONS *Options
|
||
|
);
|
||
|
|
||
|
static
|
||
|
int
|
||
|
CheckPE32File (
|
||
|
FILE *Fptr,
|
||
|
UINT16 *MachineType,
|
||
|
UINT16 *SubSystem
|
||
|
);
|
||
|
|
||
|
static
|
||
|
int
|
||
|
ProcessEfiFile (
|
||
|
FILE *OutFptr,
|
||
|
FILE_LIST *InFile,
|
||
|
UINT16 VendId,
|
||
|
UINT16 DevId,
|
||
|
UINT32 *Size
|
||
|
);
|
||
|
|
||
|
static
|
||
|
int
|
||
|
ProcessBinFile (
|
||
|
FILE *OutFptr,
|
||
|
FILE_LIST *InFile,
|
||
|
UINT32 *Size
|
||
|
);
|
||
|
|
||
|
static
|
||
|
void
|
||
|
DumpImage (
|
||
|
FILE_LIST *InFile
|
||
|
);
|
||
|
|
||
|
char *
|
||
|
GetMachineTypeStr (
|
||
|
UINT16 MachineType
|
||
|
);
|
||
|
|
||
|
static
|
||
|
char *
|
||
|
GetSubsystemTypeStr (
|
||
|
UINT16 SubsystemType
|
||
|
);
|
||
|
|
||
|
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
|
||
|
|
||
|
--*/
|
||
|
// GC_TODO: ] - add argument and description to function comment
|
||
|
{
|
||
|
INT8 *Ext;
|
||
|
FILE *FptrOut;
|
||
|
UINT32 Status;
|
||
|
FILE_LIST *FList;
|
||
|
UINT32 TotalSize;
|
||
|
UINT32 Size;
|
||
|
|
||
|
Status = STATUS_SUCCESS;
|
||
|
FptrOut = NULL;
|
||
|
|
||
|
//
|
||
|
// Parse the command line arguments
|
||
|
//
|
||
|
if (ParseCommandLine (Argc, Argv, &mOptions)) {
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
//
|
||
|
// If dumping an image, then do that and quit
|
||
|
//
|
||
|
if (mOptions.DumpOption) {
|
||
|
DumpImage (mOptions.FileList);
|
||
|
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]) {
|
||
|
strcpy (mOptions.OutFileName, mOptions.FileList->FileName);
|
||
|
//
|
||
|
// Find the last . on the line and replace the filename extension with
|
||
|
// the default
|
||
|
//
|
||
|
for (Ext = mOptions.OutFileName + strlen (mOptions.OutFileName) - 1;
|
||
|
(Ext >= mOptions.OutFileName) && (*Ext != '.') && (*Ext != '\\');
|
||
|
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;
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Input and output file names must be different - %s = %s\n",
|
||
|
FList->FileName,
|
||
|
mOptions.OutFileName
|
||
|
);
|
||
|
goto BailOut;
|
||
|
}
|
||
|
}
|
||
|
//
|
||
|
// Now open our output file
|
||
|
//
|
||
|
if ((FptrOut = fopen (mOptions.OutFileName, "w+b")) == NULL) {
|
||
|
fprintf (stdout, "ERROR: Failed to open output file %s\n", 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) {
|
||
|
if (mOptions.Verbose) {
|
||
|
fprintf (stdout, "Processing EFI file %s\n", FList->FileName);
|
||
|
}
|
||
|
|
||
|
Status = ProcessEfiFile (FptrOut, FList, mOptions.VendId, mOptions.DevId, &Size);
|
||
|
} else if (FList->FileFlags & FILE_FLAG_BINARY) {
|
||
|
if (mOptions.Verbose) {
|
||
|
fprintf (stdout, "Processing binary file %s\n", FList->FileName);
|
||
|
}
|
||
|
|
||
|
Status = ProcessBinFile (FptrOut, FList, &Size);
|
||
|
} else {
|
||
|
fprintf (stdout, "ERROR: File not specified as EFI or binary: %s\n", FList->FileName);
|
||
|
Status = STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
if (mOptions.Verbose) {
|
||
|
fprintf (stdout, " Output size = 0x%X\n", Size);
|
||
|
}
|
||
|
|
||
|
if (Status != STATUS_SUCCESS) {
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
TotalSize += Size;
|
||
|
}
|
||
|
//
|
||
|
// Check total size
|
||
|
//
|
||
|
if (TotalSize > MAX_OPTION_ROM_SIZE) {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Option ROM image size exceeds limit 0x%X bytes\n",
|
||
|
MAX_OPTION_ROM_SIZE
|
||
|
);
|
||
|
Status = STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
BailOut:
|
||
|
if (FptrOut != NULL) {
|
||
|
fclose (FptrOut);
|
||
|
}
|
||
|
//
|
||
|
// Clean up our file list
|
||
|
//
|
||
|
while (mOptions.FileList != NULL) {
|
||
|
FList = mOptions.FileList->Next;
|
||
|
free (mOptions.FileList);
|
||
|
mOptions.FileList = FList;
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
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 *PciDs;
|
||
|
UINT32 Index;
|
||
|
UINT8 ByteCheckSum;
|
||
|
|
||
|
Status = STATUS_SUCCESS;
|
||
|
|
||
|
//
|
||
|
// Try to open the input file
|
||
|
//
|
||
|
if ((InFptr = fopen (InFile->FileName, "rb")) == NULL) {
|
||
|
fprintf (stdout, "ERROR: Failed to open input file %s\n", 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) {
|
||
|
fprintf (stdout, " File size = 0x%X\n", FileSize);
|
||
|
}
|
||
|
|
||
|
fseek (InFptr, 0, SEEK_SET);
|
||
|
Buffer = (INT8 *) malloc (FileSize);
|
||
|
if (Buffer == NULL) {
|
||
|
fprintf (stdout, "ERROR: Memory allocation failed\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
if (fread (Buffer, FileSize, 1, InFptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to read all bytes from input file\n");
|
||
|
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) {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Option ROM image %s size exceeds limit 0x%X bytes\n",
|
||
|
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) {
|
||
|
fprintf (stdout, "ERROR: ROM image file has invalid ROM signature\n");
|
||
|
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)) {
|
||
|
fprintf (stdout, "ERROR: Invalid PCI data structure offset\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
PciDs = (PCI_DATA_STRUCTURE *) (Buffer + RomHdr->PcirOffset);
|
||
|
if (PciDs->Signature != PCI_DATA_STRUCTURE_SIGNATURE) {
|
||
|
fprintf (stdout, "ERROR: PCI data structure has invalid signature\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
//
|
||
|
// If this is the last image, then set the LAST bit unless requested not
|
||
|
// to via the command-line -l argument. Otherwise, make sure you clear it.
|
||
|
//
|
||
|
if ((InFile->Next == NULL) && (mOptions.NoLast == 0)) {
|
||
|
PciDs->Indicator = INDICATOR_LAST;
|
||
|
} else {
|
||
|
PciDs->Indicator = 0;
|
||
|
}
|
||
|
|
||
|
ByteCheckSum = 0;
|
||
|
for (Index = 0; Index < FileSize - 1; Index++) {
|
||
|
ByteCheckSum = (UINT8) (ByteCheckSum + Buffer[Index]);
|
||
|
}
|
||
|
|
||
|
Buffer[FileSize - 1] = (UINT8) ((~ByteCheckSum) + 1);
|
||
|
fprintf (stdout, "CheckSUm = %02x\n", (UINT32) Buffer[FileSize - 1]);
|
||
|
|
||
|
//
|
||
|
// Now copy the input file contents out to the output file
|
||
|
//
|
||
|
if (fwrite (Buffer, FileSize, 1, OutFptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to write all file bytes to output file\n");
|
||
|
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) {
|
||
|
fprintf (stdout, "Error processing binary file %s\n", 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 PciDs;
|
||
|
UINT32 FileSize;
|
||
|
UINT32 CompressedFileSize;
|
||
|
UINT8 *Buffer;
|
||
|
UINT8 *CompressedBuffer;
|
||
|
UINT8 *TempBufferPtr;
|
||
|
UINT32 TotalSize;
|
||
|
UINT32 HeaderSize;
|
||
|
UINT16 MachineType;
|
||
|
UINT16 SubSystem;
|
||
|
UINT32 HeaderPadBytes;
|
||
|
|
||
|
//
|
||
|
// Try to open the input file
|
||
|
//
|
||
|
if ((InFptr = fopen (InFile->FileName, "rb")) == NULL) {
|
||
|
fprintf (stdout, "ERROR: Failed to open input file %s\n", 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;
|
||
|
}
|
||
|
|
||
|
HeaderSize = sizeof (PCI_DATA_STRUCTURE) + HeaderPadBytes + sizeof (EFI_PCI_EXPANSION_ROM_HEADER);
|
||
|
if (mOptions.Verbose) {
|
||
|
fprintf (stdout, " File size = 0x%X\n", 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 = (INT8 *) malloc (FileSize);
|
||
|
if (Buffer == NULL) {
|
||
|
fprintf (stdout, "ERROR: Memory allocation failed\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
fseek (InFptr, 0, SEEK_SET);
|
||
|
if (fread (Buffer, FileSize, 1, InFptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to read all bytes from input file\n");
|
||
|
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) {
|
||
|
//
|
||
|
// Allocate a buffer into which we can compress the image, compress it,
|
||
|
// and use that size as the new size.
|
||
|
//
|
||
|
CompressedBuffer = (INT8 *) malloc (FileSize);
|
||
|
if (CompressedBuffer == NULL) {
|
||
|
fprintf (stdout, "ERROR: Memory allocation failed\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
CompressedFileSize = FileSize;
|
||
|
Status = EfiCompress (Buffer, FileSize, CompressedBuffer, &CompressedFileSize);
|
||
|
if (Status != STATUS_SUCCESS) {
|
||
|
fprintf (stdout, "ERROR: Compression failed\n");
|
||
|
goto BailOut;
|
||
|
}
|
||
|
//
|
||
|
// Now compute the size, then swap buffer pointers.
|
||
|
//
|
||
|
if (mOptions.Verbose) {
|
||
|
fprintf (stdout, " Comp size = 0x%X\n", 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;
|
||
|
}
|
||
|
//
|
||
|
// Check size
|
||
|
//
|
||
|
if (TotalSize > MAX_OPTION_ROM_SIZE) {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Option ROM image %s size exceeds limit 0x%X bytes\n",
|
||
|
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;
|
||
|
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
|
||
|
//
|
||
|
memset (&PciDs, 0, sizeof (PCI_DATA_STRUCTURE));
|
||
|
|
||
|
PciDs.Signature = PCI_DATA_STRUCTURE_SIGNATURE;
|
||
|
PciDs.VendorId = VendId;
|
||
|
PciDs.DeviceId = DevId;
|
||
|
PciDs.Length = (UINT16) sizeof (PCI_DATA_STRUCTURE);
|
||
|
PciDs.Revision = 0;
|
||
|
//
|
||
|
// Class code and code revision from the command line (optional)
|
||
|
//
|
||
|
PciDs.ClassCode[0] = (UINT8) InFile->ClassCode;
|
||
|
PciDs.ClassCode[1] = (UINT8) (InFile->ClassCode >> 8);
|
||
|
PciDs.ClassCode[2] = (UINT8) (InFile->ClassCode >> 16);
|
||
|
PciDs.ImageLength = RomHdr.InitializationSize;
|
||
|
PciDs.CodeRevision = InFile->CodeRevision;
|
||
|
PciDs.CodeType = PCI_CODE_TYPE_EFI_IMAGE;
|
||
|
|
||
|
//
|
||
|
// If this is the last image, then set the LAST bit unless requested not
|
||
|
// to via the command-line -l argument.
|
||
|
//
|
||
|
if ((InFile->Next == NULL) && (mOptions.NoLast == 0)) {
|
||
|
PciDs.Indicator = INDICATOR_LAST;
|
||
|
}
|
||
|
//
|
||
|
// Write the ROM header to the output file
|
||
|
//
|
||
|
if (fwrite (&RomHdr, sizeof (RomHdr), 1, OutFptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to write ROM header to output file\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write pad bytes to align the PciDs
|
||
|
//
|
||
|
while (HeaderPadBytes > 0) {
|
||
|
if (putc (0, OutFptr) == EOF) {
|
||
|
fprintf (stdout, "ERROR: Failed to write ROM header pad bytes to output file\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
HeaderPadBytes--;
|
||
|
}
|
||
|
//
|
||
|
// Write the PCI data structure header to the output file
|
||
|
//
|
||
|
if (fwrite (&PciDs, sizeof (PciDs), 1, OutFptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to write PCI ROM header to output file\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
//
|
||
|
// Keep track of how many bytes left to write
|
||
|
//
|
||
|
TotalSize -= HeaderSize;
|
||
|
|
||
|
//
|
||
|
// Now dump the input file's contents to the output file
|
||
|
//
|
||
|
if (fwrite (Buffer, FileSize, 1, OutFptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to write all file bytes to output file\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
TotalSize -= FileSize;
|
||
|
//
|
||
|
// Pad the rest of the image to make it a multiple of 512 bytes
|
||
|
//
|
||
|
while (TotalSize > 0) {
|
||
|
if (putc (~0, OutFptr) == EOF) {
|
||
|
fprintf (stdout, "ERROR: Failed to write trailing pad bytes output file\n");
|
||
|
Status = STATUS_ERROR;
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
TotalSize--;
|
||
|
}
|
||
|
|
||
|
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) {
|
||
|
fprintf (stdout, "Error processing EFI file %s\n", InFile->FileName);
|
||
|
}
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
int
|
||
|
CheckPE32File (
|
||
|
FILE *Fptr,
|
||
|
UINT16 *MachineType,
|
||
|
UINT16 *SubSystem
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
GC_TODO: Add function description
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Fptr - GC_TODO: add argument description
|
||
|
MachineType - GC_TODO: add argument description
|
||
|
SubSystem - GC_TODO: add argument description
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
GC_TODO: add return values
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
/*++
|
||
|
|
||
|
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_FILE_HEADER FileHdr;
|
||
|
EFI_IMAGE_OPTIONAL_HEADER OptionalHdr;
|
||
|
UINT32 PESig;
|
||
|
|
||
|
//
|
||
|
// Position to the start of the file
|
||
|
//
|
||
|
fseek (Fptr, 0, SEEK_SET);
|
||
|
|
||
|
//
|
||
|
// Read the DOS header
|
||
|
//
|
||
|
if (fread (&DosHeader, sizeof (DosHeader), 1, Fptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to read the DOS stub from the input file\n");
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
//
|
||
|
// Check the magic number (0x5A4D)
|
||
|
//
|
||
|
if (DosHeader.e_magic != EFI_IMAGE_DOS_SIGNATURE) {
|
||
|
fprintf (stdout, "ERROR: Input file does not appear to be a PE32 image (magic number)\n");
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
//
|
||
|
// Position into the file and check the PE signature
|
||
|
//
|
||
|
fseek (Fptr, (long) DosHeader.e_lfanew, SEEK_SET);
|
||
|
if (fread (&PESig, sizeof (PESig), 1, Fptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to read PE signature bytes from input file\n");
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
//
|
||
|
// Check the PE signature in the header "PE\0\0"
|
||
|
//
|
||
|
if (PESig != EFI_IMAGE_NT_SIGNATURE) {
|
||
|
fprintf (stdout, "ERROR: Input file does not appear to be a PE32 image (signature)\n");
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
//
|
||
|
// Read the file header and stuff their MachineType
|
||
|
//
|
||
|
if (fread (&FileHdr, sizeof (FileHdr), 1, Fptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to read PE file header from input file\n");
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
memcpy ((char *) MachineType, &FileHdr.Machine, 2);
|
||
|
|
||
|
//
|
||
|
// Read the optional header so we can get the subsystem
|
||
|
//
|
||
|
if (fread (&OptionalHdr, sizeof (OptionalHdr), 1, Fptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to read COFF optional header from input file\n");
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
*SubSystem = OptionalHdr.Subsystem;
|
||
|
if (mOptions.Verbose) {
|
||
|
fprintf (stdout, " Got subsystem = 0x%X from image\n", (int) *SubSystem);
|
||
|
}
|
||
|
//
|
||
|
// Good to go
|
||
|
//
|
||
|
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;
|
||
|
|
||
|
FileFlags = 0;
|
||
|
|
||
|
//
|
||
|
// Clear out the options
|
||
|
//
|
||
|
memset ((char *) Options, 0, sizeof (OPTIONS));
|
||
|
|
||
|
//
|
||
|
// To avoid compile warnings
|
||
|
//
|
||
|
FileList = PrevFileList = NULL;
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
//
|
||
|
// Process until no more arguments
|
||
|
//
|
||
|
while (Argc > 0) {
|
||
|
if ((Argv[0][0] == '-') || (Argv[0][0] == '/')) {
|
||
|
//
|
||
|
// To simplify string comparisons, replace slashes with dashes
|
||
|
//
|
||
|
Argv[0][0] = '-';
|
||
|
|
||
|
//
|
||
|
// Vendor ID specified with -v
|
||
|
//
|
||
|
if (_stricmp (Argv[0], "-v") == 0) {
|
||
|
//
|
||
|
// Make sure there's another parameter
|
||
|
//
|
||
|
if (Argc > 1) {
|
||
|
Options->VendId = (UINT16) strtol (Argv[1], NULL, 16);
|
||
|
Options->VendIdValid = 1;
|
||
|
} else {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Missing Vendor ID with %s\n\n",
|
||
|
Argv[0]
|
||
|
);
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
Argv++;
|
||
|
Argc--;
|
||
|
} else if (_stricmp (Argv[0], "-d") == 0) {
|
||
|
//
|
||
|
// Device ID specified with -d
|
||
|
// Make sure there's another parameter
|
||
|
//
|
||
|
if (Argc > 1) {
|
||
|
Options->DevId = (UINT16) strtol (Argv[1], NULL, 16);
|
||
|
Options->DevIdValid = 1;
|
||
|
} else {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Missing Device ID with %s\n\n",
|
||
|
Argv[0]
|
||
|
);
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
Argv++;
|
||
|
Argc--;
|
||
|
} else if (_stricmp (Argv[0], "-o") == 0) {
|
||
|
//
|
||
|
// Output filename specified with -o
|
||
|
// Make sure there's another parameter
|
||
|
//
|
||
|
if (Argc > 1) {
|
||
|
strcpy (Options->OutFileName, Argv[1]);
|
||
|
} else {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Missing output file name with %s\n\n",
|
||
|
Argv[0]
|
||
|
);
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
Argv++;
|
||
|
Argc--;
|
||
|
} else if ((_stricmp (Argv[0], "-h") == 0) || (strcmp (Argv[0], "-?") == 0)) {
|
||
|
//
|
||
|
// Help option
|
||
|
//
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
} else if (_stricmp (Argv[0], "-b") == 0) {
|
||
|
//
|
||
|
// Specify binary files with -b
|
||
|
//
|
||
|
FileFlags = (FileFlags &~FILE_FLAG_EFI) | FILE_FLAG_BINARY;
|
||
|
} else if ((_stricmp (Argv[0], "-e") == 0) || (_stricmp (Argv[0], "-ec") == 0)) {
|
||
|
//
|
||
|
// Specify EFI files with -e. Specify EFI-compressed with -ec.
|
||
|
//
|
||
|
FileFlags = (FileFlags &~FILE_FLAG_BINARY) | 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 -l
|
||
|
//
|
||
|
} else if (_stricmp (Argv[0], "-l") == 0) {
|
||
|
Options->NoLast = 1;
|
||
|
} else if (_stricmp (Argv[0], "-p") == 0) {
|
||
|
//
|
||
|
// -v for verbose would have been nicer, but it's already used. Let's use
|
||
|
// -p for prolix (wordy) output
|
||
|
//
|
||
|
Options->Verbose = 1;
|
||
|
} else if (_stricmp (Argv[0], "-dump") == 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->DevIdValid = 1;
|
||
|
FileFlags = FILE_FLAG_BINARY;
|
||
|
} else if (_stricmp (Argv[0], "-cc") == 0) {
|
||
|
//
|
||
|
// Class code value for the next file in the list.
|
||
|
// Make sure there's another parameter
|
||
|
//
|
||
|
if (Argc > 1) {
|
||
|
//
|
||
|
// No error checking on the return value. Could check for LONG_MAX,
|
||
|
// LONG_MIN, or 0 class code value if desired. Check range (3 bytes)
|
||
|
// at least.
|
||
|
//
|
||
|
ClassCode = (UINT32) strtol (Argv[1], NULL, 16);
|
||
|
if (ClassCode & 0xFF000000) {
|
||
|
fprintf (stdout, "ERROR: Class code %s out of range\n", Argv[1]);
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
} else {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Missing class code value with %s\n\n",
|
||
|
Argv[0]
|
||
|
);
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
Argv++;
|
||
|
Argc--;
|
||
|
} else if (_stricmp (Argv[0], "-rev") == 0) {
|
||
|
//
|
||
|
// Code revision in the PCI data structure. The value is for the next
|
||
|
// file in the list.
|
||
|
// Make sure there's another parameter
|
||
|
//
|
||
|
if (Argc > 1) {
|
||
|
//
|
||
|
// No error checking on the return value. Could check for LONG_MAX,
|
||
|
// LONG_MIN, or 0 value if desired. Check range (2 bytes)
|
||
|
// at least.
|
||
|
//
|
||
|
CodeRevision = (UINT32) strtol (Argv[1], NULL, 16);
|
||
|
if (CodeRevision & 0xFFFF0000) {
|
||
|
fprintf (stdout, "ERROR: Code revision %s out of range\n", Argv[1]);
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
} else {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Missing code revision value with %s\n\n",
|
||
|
Argv[0]
|
||
|
);
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
Argv++;
|
||
|
Argc--;
|
||
|
} else {
|
||
|
fprintf (stdout, "ERROR: Invalid option specified: %s\n\n", Argv[0]);
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
} 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) {
|
||
|
fprintf (stdout, "ERROR: Missing -e or -b with input file %s\n", Argv[0]);
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
//
|
||
|
// Create a new file structure
|
||
|
//
|
||
|
FileList = (FILE_LIST *) malloc (sizeof (FILE_LIST));
|
||
|
if (FileList == NULL) {
|
||
|
fprintf (stdout, "ERROR: Memory allocation failure\n");
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
memset ((char *) FileList, 0, sizeof (FILE_LIST));
|
||
|
FileList->FileName = Argv[0];
|
||
|
FileList->FileFlags = FileFlags;
|
||
|
if (Options->FileList == NULL) {
|
||
|
Options->FileList = FileList;
|
||
|
} else {
|
||
|
if (PrevFileList == NULL) {
|
||
|
PrevFileList = FileList;
|
||
|
} else {
|
||
|
PrevFileList->Next = FileList;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PrevFileList = FileList;
|
||
|
//
|
||
|
// Set the class code and code revision for this file, then reset the values.
|
||
|
//
|
||
|
FileList->ClassCode = ClassCode;
|
||
|
FileList->CodeRevision = (UINT16) CodeRevision;
|
||
|
ClassCode = 0;
|
||
|
CodeRevision = 0;
|
||
|
}
|
||
|
//
|
||
|
// Next argument
|
||
|
//
|
||
|
Argv++;
|
||
|
Argc--;
|
||
|
}
|
||
|
//
|
||
|
// Make sure they specified a device ID and vendor ID
|
||
|
//
|
||
|
if (!Options->VendIdValid) {
|
||
|
fprintf (stdout, "ERROR: Missing Vendor ID on command line\n\n");
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
if (!Options->DevIdValid) {
|
||
|
fprintf (stdout, "ERROR: Missing Device ID on command line\n\n");
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
//
|
||
|
// Must have specified some files
|
||
|
//
|
||
|
if (Options->FileList == NULL) {
|
||
|
fprintf (stdout, "ERROR: Missing input file name\n");
|
||
|
Usage ();
|
||
|
return STATUS_ERROR;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static
|
||
|
void
|
||
|
Usage (
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Print usage information for this utility.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None.
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
Nothing.
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
int Index;
|
||
|
static const char *Msg[] = {
|
||
|
"EfiRom "UTILITY_VERSION" - Intel EFI Make Option ROM utility",
|
||
|
" Copyright (C), 1999 - 2002 Intel Coproration\n",
|
||
|
" Create an option ROM image from a list of input files",
|
||
|
" Usage: efirom {-p} [-v VendorId] [-d DeviceId] {-o OutFileName} ",
|
||
|
" [-e|-b] [FileName(s)]",
|
||
|
" where:",
|
||
|
" VendorId - required hex PCI Vendor ID for the device",
|
||
|
" DeviceId - required hex PCI Device ID for the device",
|
||
|
" OutFileName - optional output file name. Default is the first input",
|
||
|
" file name with a "DEFAULT_OUTPUT_EXTENSION" file extension",
|
||
|
" FileNames - input PE32 or binary file name(s)",
|
||
|
" BinFileName - input binary file name(s)",
|
||
|
" -p - for verbose output",
|
||
|
" -l - to not automatically set the LAST bit on the last file",
|
||
|
" -b - following FileNames are binary files",
|
||
|
" -e - following FileNames are EFI PE32 image files",
|
||
|
" -ec - following FileNames are EFI PE32 image files, and should",
|
||
|
" be compressed by this utility",
|
||
|
" -cc ClassCode - to use hex ClassCode in the PCI data structure header for",
|
||
|
" the following FileName",
|
||
|
" -rev Revision - to use hex Revision in the PCI data structure header for",
|
||
|
" the following FileName",
|
||
|
" -dump - to dump the headers of an existing option ROM image",
|
||
|
"",
|
||
|
"Example usage: EfiRom -v 0xABCD -d 0x1234 -b File1.bin File2.bin -e File1.efi File2.efi ",
|
||
|
"",
|
||
|
NULL
|
||
|
};
|
||
|
|
||
|
for (Index = 0; Msg[Index] != NULL; Index++) {
|
||
|
fprintf (stdout, "%s\n", Msg[Index]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static
|
||
|
void
|
||
|
DumpImage (
|
||
|
FILE_LIST *InFile
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
GC_TODO: Add function description
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
InFile - GC_TODO: add argument description
|
||
|
|
||
|
Returns:
|
||
|
|
||
|
GC_TODO: add return values
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
PCI_EXPANSION_ROM_HEADER PciRomHdr;
|
||
|
FILE *InFptr;
|
||
|
UINT32 ImageStart;
|
||
|
UINT32 ImageCount;
|
||
|
EFI_PCI_EXPANSION_ROM_HEADER EfiRomHdr;
|
||
|
PCI_DATA_STRUCTURE PciDs;
|
||
|
|
||
|
//
|
||
|
// Open the input file
|
||
|
//
|
||
|
if ((InFptr = fopen (InFile->FileName, "rb")) == NULL) {
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
"ERROR: Could not open input file %s\n",
|
||
|
InFile->FileName
|
||
|
);
|
||
|
return ;
|
||
|
}
|
||
|
//
|
||
|
// Go through the image and dump the header stuff for each
|
||
|
//
|
||
|
ImageCount = 0;
|
||
|
for (;;) {
|
||
|
//
|
||
|
// Save our postition 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) {
|
||
|
fprintf (stdout, "ERROR: Failed to read PCI ROM header from file\n");
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Dump the contents of the header
|
||
|
//
|
||
|
fprintf (stdout, "Image %d -- Offset 0x%X\n", ImageCount, ImageStart);
|
||
|
fprintf (stdout, " ROM header contents\n");
|
||
|
fprintf (stdout, " Signature 0x%04X\n", (UINT32) PciRomHdr.Signature);
|
||
|
fprintf (stdout, " PCIR offset 0x%04X\n", (UINT32) PciRomHdr.PcirOffset);
|
||
|
//
|
||
|
// Find PCI data structure
|
||
|
//
|
||
|
if (fseek (InFptr, ImageStart + PciRomHdr.PcirOffset, SEEK_SET)) {
|
||
|
fprintf (stdout, "ERROR: Failed to seek to PCI data structure\n");
|
||
|
goto BailOut;
|
||
|
}
|
||
|
//
|
||
|
// Read and dump the PCI data structure
|
||
|
//
|
||
|
if (fread (&PciDs, sizeof (PciDs), 1, InFptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to read PCI data structure from file\n");
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
fprintf (stdout, " PCI Data Structure\n");
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
" Signature %c%c%c%c\n",
|
||
|
(char) PciDs.Signature,
|
||
|
(char) (PciDs.Signature >> 8),
|
||
|
(char) (PciDs.Signature >> 16),
|
||
|
(char) (PciDs.Signature >> 24)
|
||
|
);
|
||
|
fprintf (stdout, " Vendor ID 0x%04X\n", PciDs.VendorId);
|
||
|
fprintf (stdout, " Device ID 0x%04X\n", PciDs.DeviceId);
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
" Class Code 0x%06X\n",
|
||
|
(UINT32) (PciDs.ClassCode[0] | (PciDs.ClassCode[1] << 8) | (PciDs.ClassCode[2] << 16))
|
||
|
);
|
||
|
fprintf (stdout, " Image size 0x%X\n", PciDs.ImageLength * 512);
|
||
|
fprintf (stdout, " Code revision: 0x%04X\n", PciDs.CodeRevision);
|
||
|
fprintf (stdout, " Indicator 0x%02X", (UINT32) PciDs.Indicator);
|
||
|
//
|
||
|
// Print the indicator, used to flag the last image
|
||
|
//
|
||
|
if (PciDs.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.
|
||
|
//
|
||
|
fprintf (stdout, " Code type 0x%02X", (UINT32) PciDs.CodeType);
|
||
|
if (PciDs.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)) {
|
||
|
fprintf (stdout, "ERROR: Failed to re-seek to ROM header structure\n");
|
||
|
goto BailOut;
|
||
|
}
|
||
|
|
||
|
if (fread (&EfiRomHdr, sizeof (EfiRomHdr), 1, InFptr) != 1) {
|
||
|
fprintf (stdout, "ERROR: Failed to read EFI PCI ROM header from file\n");
|
||
|
goto BailOut;
|
||
|
}
|
||
|
//
|
||
|
// Now dump more info
|
||
|
//
|
||
|
fprintf (stdout, " EFI Signature 0x%04X\n", EfiRomHdr.EfiSignature);
|
||
|
fprintf (
|
||
|
stdout,
|
||
|
" Compression Type 0x%04X ",
|
||
|
(UINT32) 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",
|
||
|
(UINT32) EfiRomHdr.EfiImageHeaderOffset,
|
||
|
(UINT32) (EfiRomHdr.EfiImageHeaderOffset + 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 (PciDs.Indicator == INDICATOR_LAST) {
|
||
|
goto BailOut;
|
||
|
}
|
||
|
//
|
||
|
// Seek to the start of the next image
|
||
|
//
|
||
|
if (fseek (InFptr, ImageStart + (PciDs.ImageLength * 512), SEEK_SET)) {
|
||
|
fprintf (stdout, "ERROR: Failed to seek to next image\n");
|
||
|
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";
|
||
|
}
|