2 * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include <gpxe/efi/efi.h>
29 #include <gpxe/efi/IndustryStandard/PeImage.h>
30 #include <gpxe/efi/IndustryStandard/Pci22.h>
32 #define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
34 /** Command-line options */
43 * @v len Length of memory to allocate
44 * @ret ptr Pointer to allocated memory
46 static void * xmalloc ( size_t len ) {
51 eprintf ( "Could not allocate %zd bytes\n", len );
64 static size_t file_size ( FILE *file ) {
67 if ( fseek ( file, 0, SEEK_END ) != 0 ) {
68 eprintf ( "Could not seek: %s\n", strerror ( errno ) );
73 eprintf ( "Could not determine file size: %s\n",
85 * @v len Length to copy
87 static void file_copy ( FILE *in, FILE *out, size_t len ) {
93 if ( frag_len > sizeof ( buf ) )
94 frag_len = sizeof ( buf );
95 if ( fread ( buf, frag_len, 1, in ) != 1 ) {
96 eprintf ( "Could not read: %s\n",
100 if ( fwrite ( buf, frag_len, 1, out ) != 1 ) {
101 eprintf ( "Could not write: %s\n",
102 strerror ( errno ) );
110 * Read information from PE headers
113 * @ret machine Machine type
114 * @ret subsystem EFI subsystem
116 static void read_pe_info ( FILE *pe, uint16_t *machine,
117 uint16_t *subsystem ) {
118 EFI_IMAGE_DOS_HEADER dos;
120 EFI_IMAGE_NT_HEADERS32 nt32;
121 EFI_IMAGE_NT_HEADERS64 nt64;
124 /* Read DOS header */
125 if ( fseek ( pe, 0, SEEK_SET ) != 0 ) {
126 eprintf ( "Could not seek: %s\n", strerror ( errno ) );
129 if ( fread ( &dos, sizeof ( dos ), 1, pe ) != 1 ) {
130 eprintf ( "Could not read: %s\n", strerror ( errno ) );
135 if ( fseek ( pe, dos.e_lfanew, SEEK_SET ) != 0 ) {
136 eprintf ( "Could not seek: %s\n", strerror ( errno ) );
139 if ( fread ( &nt, sizeof ( nt ), 1, pe ) != 1 ) {
140 eprintf ( "Could not read: %s\n", strerror ( errno ) );
144 /* Locate NT header */
145 *machine = nt.nt32.FileHeader.Machine;
146 switch ( *machine ) {
147 case EFI_IMAGE_MACHINE_IA32:
148 *subsystem = nt.nt32.OptionalHeader.Subsystem;
150 case EFI_IMAGE_MACHINE_X64:
151 *subsystem = nt.nt64.OptionalHeader.Subsystem;
154 eprintf ( "Unrecognised machine type %04x\n", *machine );
160 * Convert EFI image to ROM image
165 static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) {
167 EFI_PCI_EXPANSION_ROM_HEADER rom;
168 PCI_DATA_STRUCTURE pci __attribute__ (( aligned ( 4 ) ));
172 unsigned int rom_size_sectors;
174 /* Determine output file size */
175 pe_size = file_size ( pe );
176 rom_size = ( pe_size + sizeof ( headers ) );
177 rom_size_sectors = ( ( rom_size + 511 ) / 512 );
179 /* Construct ROM header */
180 memset ( &headers, 0, sizeof ( headers ) );
181 headers.rom.Signature = PCI_EXPANSION_ROM_HEADER_SIGNATURE;
182 headers.rom.InitializationSize = rom_size_sectors;
183 headers.rom.EfiSignature = EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE;
184 read_pe_info ( pe, &headers.rom.EfiMachineType,
185 &headers.rom.EfiSubsystem );
186 headers.rom.EfiImageHeaderOffset = sizeof ( headers );
187 headers.rom.PcirOffset =
188 offsetof ( typeof ( headers ), pci );
189 headers.pci.Signature = PCI_DATA_STRUCTURE_SIGNATURE;
190 headers.pci.VendorId = opts->vendor;
191 headers.pci.DeviceId = opts->device;
192 headers.pci.Length = sizeof ( headers.pci );
193 headers.pci.ClassCode[0] = PCI_CLASS_NETWORK;
194 headers.pci.ImageLength = rom_size_sectors;
195 headers.pci.CodeType = 0x03; /* No constant in EFI headers? */
196 headers.pci.Indicator = 0x80; /* No constant in EFI headers? */
198 /* Write out ROM header */
199 if ( fwrite ( &headers, sizeof ( headers ), 1, rom ) != 1 ) {
200 eprintf ( "Could not write headers: %s\n",
201 strerror ( errno ) );
205 /* Write out payload */
206 if ( fseek ( pe, 0, SEEK_SET ) != 0 ) {
207 eprintf ( "Could not seek: %s\n", strerror ( errno ) );
210 file_copy ( pe, rom, pe_size );
212 /* Round up to 512-byte boundary */
213 if ( ftruncate ( fileno ( rom ), ( rom_size_sectors * 512 ) ) != 0 ) {
214 eprintf ( "Could not set length: %s\n", strerror ( errno ) );
222 * @v program_name Program name
224 static void print_help ( const char *program_name ) {
225 eprintf ( "Syntax: %s [--vendor=VVVV] [--device=DDDD] "
226 "infile outfile\n", program_name );
230 * Parse command-line options
232 * @v argc Argument count
233 * @v argv Argument list
234 * @v opts Options structure to populate
236 static int parse_options ( const int argc, char **argv,
237 struct options *opts ) {
242 int option_index = 0;
243 static struct option long_options[] = {
244 { "vendor", required_argument, NULL, 'v' },
245 { "device", required_argument, NULL, 'd' },
246 { "help", 0, NULL, 'h' },
250 if ( ( c = getopt_long ( argc, argv, "v:d:h",
252 &option_index ) ) == -1 ) {
258 opts->vendor = strtoul ( optarg, &end, 16 );
260 eprintf ( "Invalid vendor \"%s\"\n", optarg );
265 opts->device = strtoul ( optarg, &end, 16 );
267 eprintf ( "Invalid device \"%s\"\n", optarg );
272 print_help ( argv[0] );
282 int main ( int argc, char **argv ) {
283 struct options opts = {
285 unsigned int infile_index;
286 const char *infile_name;
287 const char *outfile_name;
291 /* Parse command-line arguments */
292 infile_index = parse_options ( argc, argv, &opts );
293 if ( argc != ( infile_index + 2 ) ) {
294 print_help ( argv[0] );
297 infile_name = argv[infile_index];
298 outfile_name = argv[infile_index + 1];
300 /* Open input and output files */
301 infile = fopen ( infile_name, "r" );
303 eprintf ( "Could not open %s for reading: %s\n",
304 infile_name, strerror ( errno ) );
307 outfile = fopen ( outfile_name, "w" );
309 eprintf ( "Could not open %s for writing: %s\n",
310 outfile_name, strerror ( errno ) );
315 make_efi_rom ( infile, outfile, &opts );