1/* 2 * Copyright (C) 2009 Michael Brown <mbrown@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 */ 36struct 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 */ 47static 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 */ 65static 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 */ 78static 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 */ 111static 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 */ 183static 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 */ 195static 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 241int 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