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