Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2009 Michael Brown <mbrown (at) fensystems.co.uk>.
      3  *
      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.
      8  *
      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.
     13  *
     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.
     17  */
     18 
     19 #include <stdint.h>
     20 #include <stddef.h>
     21 #include <stdlib.h>
     22 #include <stdio.h>
     23 #include <string.h>
     24 #include <sys/stat.h>
     25 #include <unistd.h>
     26 #include <errno.h>
     27 #include <assert.h>
     28 #include <getopt.h>
     29 #include <gpxe/efi/efi.h>
     30 #include <gpxe/efi/IndustryStandard/PeImage.h>
     31 #include <gpxe/efi/IndustryStandard/Pci22.h>
     32 
     33 #define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
     34 
     35 /** Command-line options */
     36 struct options {
     37 	uint16_t vendor;
     38 	uint16_t device;
     39 };
     40 
     41 /**
     42  * Allocate memory
     43  *
     44  * @v len		Length of memory to allocate
     45  * @ret ptr		Pointer to allocated memory
     46  */
     47 static void * xmalloc ( size_t len ) {
     48 	void *ptr;
     49 
     50 	ptr = malloc ( len );
     51 	if ( ! ptr ) {
     52 		eprintf ( "Could not allocate %zd bytes\n", len );
     53 		exit ( 1 );
     54 	}
     55 
     56 	return ptr;
     57 }
     58 
     59 /**
     60  * Get file size
     61  *
     62  * @v file		File
     63  * @v len		File size
     64  */
     65 static size_t file_size ( FILE *file ) {
     66 	ssize_t len;
     67 
     68 	return len;
     69 }
     70 
     71 /**
     72  * Read information from PE headers
     73  *
     74  * @v pe		PE file
     75  * @ret machine		Machine type
     76  * @ret subsystem	EFI subsystem
     77  */
     78 static void read_pe_info ( void *pe, uint16_t *machine,
     79 			   uint16_t *subsystem ) {
     80 	EFI_IMAGE_DOS_HEADER *dos;
     81 	union {
     82 		EFI_IMAGE_NT_HEADERS32 nt32;
     83 		EFI_IMAGE_NT_HEADERS64 nt64;
     84 	} *nt;
     85 
     86 	/* Locate NT header */
     87 	dos = pe;
     88 	nt = ( pe + dos->e_lfanew );
     89 
     90 	/* Parse out PE information */
     91 	*machine = nt->nt32.FileHeader.Machine;
     92 	switch ( *machine ) {
     93 	case EFI_IMAGE_MACHINE_IA32:
     94 		*subsystem = nt->nt32.OptionalHeader.Subsystem;
     95 		break;
     96 	case EFI_IMAGE_MACHINE_X64:
     97 		*subsystem = nt->nt64.OptionalHeader.Subsystem;
     98 		break;
     99 	default:
    100 		eprintf ( "Unrecognised machine type %04x\n", *machine );
    101 		exit ( 1 );
    102 	}
    103 }
    104 
    105 /**
    106  * Convert EFI image to ROM image
    107  *
    108  * @v pe		EFI file
    109  * @v rom		ROM file
    110  */
    111 static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) {
    112 	struct {
    113 		EFI_PCI_EXPANSION_ROM_HEADER rom;
    114 		PCI_DATA_STRUCTURE pci __attribute__ (( aligned ( 4 ) ));
    115 		uint8_t checksum;
    116 	} *headers;
    117 	struct stat pe_stat;
    118 	size_t pe_size;
    119 	size_t rom_size;
    120 	void *buf;
    121 	void *payload;
    122 	unsigned int i;
    123 	uint8_t checksum;
    124 
    125 	/* Determine PE file size */
    126 	if ( fstat ( fileno ( pe ), &pe_stat ) != 0 ) {
    127 		eprintf ( "Could not stat PE file: %s\n",
    128 			  strerror ( errno ) );
    129 		exit ( 1 );
    130 	}
    131 	pe_size = pe_stat.st_size;
    132 
    133 	/* Determine ROM file size */
    134 	rom_size = ( ( pe_size + sizeof ( *headers ) + 511 ) & ~511 );
    135 
    136 	/* Allocate ROM buffer and read in PE file */
    137 	buf = xmalloc ( rom_size );
    138 	memset ( buf, 0, rom_size );
    139 	headers = buf;
    140 	payload = ( buf + sizeof ( *headers ) );
    141 	if ( fread ( payload, pe_size, 1, pe ) != 1 ) {
    142 		eprintf ( "Could not read PE file: %s\n",
    143 			  strerror ( errno ) );
    144 		exit ( 1 );
    145 	}
    146 
    147 	/* Construct ROM header */
    148 	headers->rom.Signature = PCI_EXPANSION_ROM_HEADER_SIGNATURE;
    149 	headers->rom.InitializationSize = ( rom_size / 512 );
    150 	headers->rom.EfiSignature = EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE;
    151 	read_pe_info ( payload, &headers->rom.EfiMachineType,
    152 		       &headers->rom.EfiSubsystem );
    153 	headers->rom.EfiImageHeaderOffset = sizeof ( *headers );
    154 	headers->rom.PcirOffset =
    155 		offsetof ( typeof ( *headers ), pci );
    156 	headers->pci.Signature = PCI_DATA_STRUCTURE_SIGNATURE;
    157 	headers->pci.VendorId = opts->vendor;
    158 	headers->pci.DeviceId = opts->device;
    159 	headers->pci.Length = sizeof ( headers->pci );
    160 	headers->pci.ClassCode[0] = PCI_CLASS_NETWORK;
    161 	headers->pci.ImageLength = ( rom_size / 512 );
    162 	headers->pci.CodeType = 0x03; /* No constant in EFI headers? */
    163 	headers->pci.Indicator = 0x80; /* No constant in EFI headers? */
    164 
    165 	/* Fix image checksum */
    166 	for ( i = 0, checksum = 0 ; i < rom_size ; i++ )
    167 		checksum += *( ( uint8_t * ) buf + i );
    168 	headers->checksum -= checksum;
    169 
    170 	/* Write out ROM */
    171 	if ( fwrite ( buf, rom_size, 1, rom ) != 1 ) {
    172 		eprintf ( "Could not write ROM file: %s\n",
    173 			  strerror ( errno ) );
    174 		exit ( 1 );
    175 	}
    176 }
    177 
    178 /**
    179  * Print help
    180  *
    181  * @v program_name	Program name
    182  */
    183 static void print_help ( const char *program_name ) {
    184 	eprintf ( "Syntax: %s [--vendor=VVVV] [--device=DDDD] "
    185 		  "infile outfile\n", program_name );
    186 }
    187 
    188 /**
    189  * Parse command-line options
    190  *
    191  * @v argc		Argument count
    192  * @v argv		Argument list
    193  * @v opts		Options structure to populate
    194  */
    195 static int parse_options ( const int argc, char **argv,
    196 			   struct options *opts ) {
    197 	char *end;
    198 	int c;
    199 
    200 	while (1) {
    201 		int option_index = 0;
    202 		static struct option long_options[] = {
    203 			{ "vendor", required_argument, NULL, 'v' },
    204 			{ "device", required_argument, NULL, 'd' },
    205 			{ "help", 0, NULL, 'h' },
    206 			{ 0, 0, 0, 0 }
    207 		};
    208 
    209 		if ( ( c = getopt_long ( argc, argv, "v:d:h",
    210 					 long_options,
    211 					 &option_index ) ) == -1 ) {
    212 			break;
    213 		}
    214 
    215 		switch ( c ) {
    216 		case 'v':
    217 			opts->vendor = strtoul ( optarg, &end, 16 );
    218 			if ( *end ) {
    219 				eprintf ( "Invalid vendor \"%s\"\n", optarg );
    220 				exit ( 2 );
    221 			}
    222 			break;
    223 		case 'd':
    224 			opts->device = strtoul ( optarg, &end, 16 );
    225 			if ( *end ) {
    226 				eprintf ( "Invalid device \"%s\"\n", optarg );
    227 				exit ( 2 );
    228 			}
    229 			break;
    230 		case 'h':
    231 			print_help ( argv[0] );
    232 			exit ( 0 );
    233 		case '?':
    234 		default:
    235 			exit ( 2 );
    236 		}
    237 	}
    238 	return optind;
    239 }
    240 
    241 int main ( int argc, char **argv ) {
    242 	struct options opts = {
    243 	};
    244 	unsigned int infile_index;
    245 	const char *infile_name;
    246 	const char *outfile_name;
    247 	FILE *infile;
    248 	FILE *outfile;
    249 
    250 	/* Parse command-line arguments */
    251 	infile_index = parse_options ( argc, argv, &opts );
    252 	if ( argc != ( infile_index + 2 ) ) {
    253 		print_help ( argv[0] );
    254 		exit ( 2 );
    255 	}
    256 	infile_name = argv[infile_index];
    257 	outfile_name = argv[infile_index + 1];
    258 
    259 	/* Open input and output files */
    260 	infile = fopen ( infile_name, "r" );
    261 	if ( ! infile ) {
    262 		eprintf ( "Could not open %s for reading: %s\n",
    263 			  infile_name, strerror ( errno ) );
    264 		exit ( 1 );
    265 	}
    266 	outfile = fopen ( outfile_name, "w" );
    267 	if ( ! outfile ) {
    268 		eprintf ( "Could not open %s for writing: %s\n",
    269 			  outfile_name, strerror ( errno ) );
    270 		exit ( 1 );
    271 	}
    272 
    273 	/* Convert file */
    274 	make_efi_rom ( infile, outfile, &opts );
    275 
    276 	fclose ( outfile );
    277 	fclose ( infile );
    278 
    279 	return 0;
    280 }
    281