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