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