[tests] Verify ability to sleep the CPU
[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
32 #define EFI_HOSTONLY
33 #include <ipxe/efi/Uefi.h>
34 #include <ipxe/efi/IndustryStandard/PeImage.h>
35 #include <ipxe/efi/IndustryStandard/Pci22.h>
36
37 #define eprintf(...) fprintf ( stderr, __VA_ARGS__ )
38
39 /* Round up ROM size */
40 #define ROM_SIZE( len ) ( ( (len) + 511 ) & ~511 )
41
42 /* Include the EDK2 compression code */
43 #include "eficompress.c"
44
45 /** Command-line options */
46 struct options {
47 uint16_t vendor;
48 uint16_t device;
49 int compress;
50 };
51
52 /**
53 * Allocate memory
54 *
55 * @v len Length of memory to allocate
56 * @ret ptr Pointer to allocated memory
57 */
58 static void * xmalloc ( size_t len ) {
59 void *ptr;
60
61 ptr = malloc ( len );
62 if ( ! ptr ) {
63 eprintf ( "Could not allocate %zd bytes\n", len );
64 exit ( 1 );
65 }
66
67 return ptr;
68 }
69
70 /**
71 * Read information from PE headers
72 *
73 * @v pe PE file
74 * @ret machine Machine type
75 * @ret subsystem EFI subsystem
76 */
77 static void read_pe_info ( void *pe, uint16_t *machine,
78 uint16_t *subsystem ) {
79 EFI_IMAGE_DOS_HEADER *dos;
80 union {
81 EFI_IMAGE_NT_HEADERS32 nt32;
82 EFI_IMAGE_NT_HEADERS64 nt64;
83 } *nt;
84
85 /* Locate NT header */
86 dos = pe;
87 nt = ( pe + dos->e_lfanew );
88
89 /* Parse out PE information */
90 *machine = nt->nt32.FileHeader.Machine;
91 switch ( *machine ) {
92 case EFI_IMAGE_MACHINE_IA32:
93 case EFI_IMAGE_MACHINE_ARMTHUMB_MIXED:
94 *subsystem = nt->nt32.OptionalHeader.Subsystem;
95 break;
96 case EFI_IMAGE_MACHINE_X64:
97 case EFI_IMAGE_MACHINE_AARCH64:
98 *subsystem = nt->nt64.OptionalHeader.Subsystem;
99 break;
100 default:
101 eprintf ( "Unrecognised machine type %04x\n", *machine );
102 exit ( 1 );
103 }
104 }
105
106 /**
107 * Attempt to compress EFI data in-place
108 *
109 * @v data Data to be compressed
110 * @v max_len Length of data
111 * @ret len Length after attempted compression
112 */
113 static size_t efi_compress ( void *data, size_t max_len ) {
114 void *tmp;
115 UINT32 len;
116
117 /* Allocate temporary buffer for compressed data */
118 tmp = xmalloc ( max_len );
119
120 /* Attempt compression */
121 len = max_len;
122 if ( ( EfiCompress ( data, max_len, tmp, &len ) == 0 ) &&
123 ( len < max_len ) ) {
124 memcpy ( data, tmp, len );
125 } else {
126 len = max_len;
127 }
128
129 /* Free temporary buffer */
130 free ( tmp );
131
132 return len;
133 }
134
135 /**
136 * Convert EFI image to ROM image
137 *
138 * @v pe EFI file
139 * @v rom ROM file
140 */
141 static void make_efi_rom ( FILE *pe, FILE *rom, struct options *opts ) {
142 struct {
143 EFI_PCI_EXPANSION_ROM_HEADER rom;
144 PCI_DATA_STRUCTURE pci __attribute__ (( aligned ( 4 ) ));
145 uint8_t checksum;
146 } *headers;
147 struct stat pe_stat;
148 size_t pe_size;
149 size_t rom_size;
150 size_t compressed_size;
151 void *buf;
152 void *payload;
153 unsigned int i;
154 uint16_t machine;
155 uint16_t subsystem;
156 uint8_t checksum;
157 int compressed;
158
159 /* Determine PE file size */
160 if ( fstat ( fileno ( pe ), &pe_stat ) != 0 ) {
161 eprintf ( "Could not stat PE file: %s\n",
162 strerror ( errno ) );
163 exit ( 1 );
164 }
165 pe_size = pe_stat.st_size;
166
167 /* Determine ROM file size */
168 rom_size = ROM_SIZE ( sizeof ( *headers ) + pe_size );
169
170 /* Allocate ROM buffer and read in PE file */
171 buf = xmalloc ( rom_size );
172 memset ( buf, 0, rom_size );
173 headers = buf;
174 payload = ( buf + sizeof ( *headers ) );
175 if ( fread ( payload, pe_size, 1, pe ) != 1 ) {
176 eprintf ( "Could not read PE file: %s\n",
177 strerror ( errno ) );
178 exit ( 1 );
179 }
180
181 /* Parse PE headers */
182 read_pe_info ( payload, &machine, &subsystem );
183
184 /* Compress the image, if requested */
185 if ( opts->compress ) {
186 compressed_size = efi_compress ( payload, pe_size );
187 rom_size = ROM_SIZE ( sizeof ( *headers ) + compressed_size );
188 compressed = ( compressed_size < pe_size );
189 } else {
190 compressed = 0;
191 }
192
193 /* Construct ROM header */
194 headers->rom.Signature = PCI_EXPANSION_ROM_HEADER_SIGNATURE;
195 headers->rom.InitializationSize = ( rom_size / 512 );
196 headers->rom.EfiSignature = EFI_PCI_EXPANSION_ROM_HEADER_EFISIGNATURE;
197 headers->rom.EfiSubsystem = subsystem;
198 headers->rom.EfiMachineType = machine;
199 headers->rom.CompressionType =
200 ( compressed ? EFI_PCI_EXPANSION_ROM_HEADER_COMPRESSED : 0 );
201 headers->rom.EfiImageHeaderOffset = sizeof ( *headers );
202 headers->rom.PcirOffset =
203 offsetof ( typeof ( *headers ), pci );
204 headers->pci.Signature = PCI_DATA_STRUCTURE_SIGNATURE;
205 headers->pci.VendorId = opts->vendor;
206 headers->pci.DeviceId = opts->device;
207 headers->pci.Length = sizeof ( headers->pci );
208 headers->pci.ClassCode[2] = PCI_CLASS_NETWORK;
209 headers->pci.ImageLength = ( rom_size / 512 );
210 headers->pci.CodeType = 0x03; /* No constant in EFI headers? */
211 headers->pci.Indicator = 0x80; /* No constant in EFI headers? */
212
213 /* Fix image checksum */
214 for ( i = 0, checksum = 0 ; i < rom_size ; i++ )
215 checksum += *( ( uint8_t * ) buf + i );
216 headers->checksum -= checksum;
217
218 /* Write out ROM */
219 if ( fwrite ( buf, rom_size, 1, rom ) != 1 ) {
220 eprintf ( "Could not write ROM file: %s\n",
221 strerror ( errno ) );
222 exit ( 1 );
223 }
224 }
225
226 /**
227 * Print help
228 *
229 * @v program_name Program name
230 */
231 static void print_help ( const char *program_name ) {
232 eprintf ( "Syntax: %s [--vendor=VVVV] [--device=DDDD] "
233 "infile outfile\n", program_name );
234 }
235
236 /**
237 * Parse command-line options
238 *
239 * @v argc Argument count
240 * @v argv Argument list
241 * @v opts Options structure to populate
242 */
243 static int parse_options ( const int argc, char **argv,
244 struct options *opts ) {
245 char *end;
246 int c;
247
248 while (1) {
249 int option_index = 0;
250 static struct option long_options[] = {
251 { "vendor", required_argument, NULL, 'v' },
252 { "device", required_argument, NULL, 'd' },
253 { "compress", 0, NULL, 'c' },
254 { "help", 0, NULL, 'h' },
255 { 0, 0, 0, 0 }
256 };
257
258 if ( ( c = getopt_long ( argc, argv, "v:d:ch",
259 long_options,
260 &option_index ) ) == -1 ) {
261 break;
262 }
263
264 switch ( c ) {
265 case 'v':
266 opts->vendor = strtoul ( optarg, &end, 16 );
267 if ( *end || ( ! *optarg ) ) {
268 eprintf ( "Invalid vendor \"%s\"\n", optarg );
269 exit ( 2 );
270 }
271 break;
272 case 'd':
273 opts->device = strtoul ( optarg, &end, 16 );
274 if ( *end || ( ! *optarg ) ) {
275 eprintf ( "Invalid device \"%s\"\n", optarg );
276 exit ( 2 );
277 }
278 break;
279 case 'c':
280 opts->compress = 1;
281 break;
282 case 'h':
283 print_help ( argv[0] );
284 exit ( 0 );
285 case '?':
286 default:
287 exit ( 2 );
288 }
289 }
290 return optind;
291 }
292
293 int main ( int argc, char **argv ) {
294 struct options opts;
295 int infile_index;
296 const char *infile_name;
297 const char *outfile_name;
298 FILE *infile;
299 FILE *outfile;
300
301 /* Parse command-line arguments */
302 memset ( &opts, 0, sizeof ( opts ) );
303 infile_index = parse_options ( argc, argv, &opts );
304 if ( argc != ( infile_index + 2 ) ) {
305 print_help ( argv[0] );
306 exit ( 2 );
307 }
308 infile_name = argv[infile_index];
309 outfile_name = argv[infile_index + 1];
310
311 /* Open input and output files */
312 infile = fopen ( infile_name, "r" );
313 if ( ! infile ) {
314 eprintf ( "Could not open %s for reading: %s\n",
315 infile_name, strerror ( errno ) );
316 exit ( 1 );
317 }
318 outfile = fopen ( outfile_name, "w" );
319 if ( ! outfile ) {
320 eprintf ( "Could not open %s for writing: %s\n",
321 outfile_name, strerror ( errno ) );
322 exit ( 1 );
323 }
324
325 /* Convert file */
326 make_efi_rom ( infile, outfile, &opts );
327
328 fclose ( outfile );
329 fclose ( infile );
330
331 return 0;
332 }