blob: 641e1ea6c0cf574160be393cbea207ae41623443 [file] [log] [blame]
Marshall Dawson30cf1552019-03-19 14:48:33 -06001/*
2 * AMD Family 17h and later BIOS compressor
3 *
4 * Copyright (C) 2019 Advanced Micro Devices, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#include <stdio.h>
17#include <stdlib.h>
18#include <fcntl.h>
19#include <sys/stat.h>
20#include <getopt.h>
21#include <elfparsing.h>
22#include "zlib.h"
23
24#define DEBUG_FILE 0
25
26#define HDR_SIZE 256
27#define UNCOMP_MAX 0x300000
28
29#define DIR_UNDEF 0
30#define DIR_COMP 1
31#define DIR_UNCOMP 2
32
33typedef struct _header {
34 uint32_t rsvd1[5];
35 uint32_t size;
36 uint32_t rsvd2[58];
37} __attribute__((packed)) header;
38
39static const char *optstring = "i:o:cm:uh";
40
41static struct option long_options[] = {
42 {"infile", required_argument, 0, 'i' },
43 {"outfile", required_argument, 0, 'o' },
44 {"compress", required_argument, 0, 'c' },
45 {"maxsize", required_argument, 0, 'h' },
46 {"uncompress", required_argument, 0, 'u' },
47 {"help", no_argument, 0, 'h' },
48};
49
50static void usage(void)
51{
52 printf("<name>: Extract or create a zlib compressed BIOS binary\n");
53 printf(" image. A compressed image contains a 256 byte\n");
54 printf(" header with a 32-bit size at 0x14.\n");
55 printf("Usage: <name> -i in_file -o out_file -[c|u]\n");
56 printf("-i | --infile <FILE> Input file\n");
57 printf("-o | --outfile <FILE> Output file\n");
58 printf("-c | --compress Compress\n");
59 printf("-m | --maxsize <HEX_VAL> Maximum uncompressed size (optional)\n");
60 printf(" * On compress: verify uncompressed size\n");
61 printf(" will be less than or equal maxsize\n");
62 printf(" * On uncompress: override default buffer size\n");
63 printf(" allocation of 0x%x bytes\n", UNCOMP_MAX);
64 printf("-u | --uncompress Uncompress\n");
65 printf("-h | --help Display this message\n");
66
67 exit(1);
68}
69
70static int do_file(char *name, size_t *size, int oflag)
71{
72 struct stat fd_stat;
73 int fd;
74
75 fd = open(name, oflag, 0666);
76 if (fd < 0)
77 return -1;
78
79 if (fstat(fd, &fd_stat)) {
80 close(fd);
81 return -1;
82 }
83
84 if (size)
85 *size = fd_stat.st_size;
86 return fd;
87}
88
89static int parse_elf_to_xip_ram(const struct buffer *input,
90 struct buffer *output)
91{
92 struct parsed_elf pelf;
93
94 if (parse_elf(input, &pelf, ELF_PARSE_ALL))
95 return 1;
96 if (buffer_create(output, pelf.phdr->p_filesz, "") != 0)
97 return 1;
98
99 memcpy(output->data, input->data + pelf.phdr->p_offset, output->size);
100
101 return 0;
102}
103
104static int convert_elf(struct buffer *buf)
105{
106 struct buffer out;
107
108 if (parse_elf_to_xip_ram(buf, &out)) {
109 printf("\tError parsing ELF file\n");
110 return -1;
111 }
112
113 /* Discard the elf file in buf and replace with the progbits */
114 free(buf->data);
115 buf->data = out.data;
116 buf->size = out.size;
117
118 return 0;
119}
120
121static int iself(const void *input)
122{
123 const Elf32_Ehdr *ehdr = input;
124 return !memcmp(ehdr->e_ident, ELFMAG, 4);
125}
126
127/* todo: Consider using deflate() and inflate() instead of compress() and
128 * decompress(), especially if memory allocation somehow becomes a problem.
129 * Those two functions can operate on streams and process chunks of data.
130 */
131
132/* Build the required header and follow it with the compressed image. Detect
133 * whether the input is an elf image, and if so, compress only the progbits.
134 *
135 * header
136 * 0 +------+-------+-------+-------+
137 * | | | | |
138 * +----------------------+-------+
139 * | | size | | |
140 * +----------------------+-------+
141 * | | | | |
142 * | | | ... |
143 * 256 +------------------------------+
144 * |compressed image |
145 * | ... |
146 * | ... |
147 * | ... |
148 * n +------------------------------+
149 */
150static void do_compress(char *outf, char *inf, size_t max_size)
151{
152 int out_fd, in_fd;
153 struct buffer inbf, outbf;
154 int err;
155
156 in_fd = do_file(inf, &inbf.size, O_RDONLY);
157 if (in_fd < 0) {
158 printf("\tError opening input file %s\n", inf);
159 err = 1;
160 goto out;
161 }
162
163 out_fd = do_file(outf, 0, O_CREAT | O_WRONLY);
164 if (out_fd < 0) {
165 printf("\tError opening output file %s\n", outf);
166 err = 1;
167 goto out_close_in;
168 }
169
170 inbf.data = calloc(inbf.size, 1);
171 if (!inbf.data) {
172 printf("\tError allocating 0x%zx bytes for input buffer\n", inbf.size);
173 err = 1;
174 goto out_close_out;
175 }
176
177 if (read(in_fd, inbf.data, inbf.size) != (ssize_t)inbf.size) {
178 printf("\tError reading input file %s\n", inf);
179 err = 1;
180 goto out_free_in;
181 }
182
183 if (iself(inbf.data)) {
184 if (convert_elf(&inbf)) {
185 err = 1;
186 goto out_free_in;
187 }
188 }
189
190 if (max_size && inbf.size > max_size) {
191 printf("\tError - size (%zx) exceeds specified max_size (%zx)\n",
192 inbf.size, max_size);
193 err = 1;
194 goto out_free_in;
195 }
196
197 outbf.size = inbf.size; /* todo: tbd worst case? */
198 outbf.size += sizeof(header);
199 outbf.data = calloc(outbf.size, 1);
200 if (!outbf.size) {
201 printf("\tError allocating 0x%zx bytes for output buffer\n", outbf.size);
202 err = 1;
203 goto out_free_in;
204 }
205
206 err = compress((Bytef *)(outbf.data + sizeof(header)), &outbf.size,
207 (Bytef *)inbf.data, inbf.size);
208 if (err != Z_OK) {
209 printf("\tzlib compression error %d\n", err);
210 err = 1;
211 goto out_free_out;
212 }
213
214 if (DEBUG_FILE)
215 printf("\tCompressed 0x%zx bytes into 0x%zx\n", inbf.size,
216 outbf.size - sizeof(header));
217
218 ((header *)outbf.data)->size = outbf.size;
219
220 if (write(out_fd, outbf.data, outbf.size + sizeof(header))
221 != (ssize_t)(outbf.size + sizeof(header))) {
222 printf("\tError writing to %s\n", outf);
223 err = 1;
224 /* fall through to out_free_out */
225 }
226
227out_free_out:
228 free(outbf.data);
229out_free_in:
230 free(inbf.data);
231out_close_out:
232 close(out_fd);
233out_close_in:
234 close(in_fd);
235out:
236 if (err)
237 exit(err);
238}
239
240static void do_uncompress(char *outf, char *inf, size_t max_size)
241{
242 int out_fd, in_fd;
243 char *in_buf, *out_buf;
244 size_t size_unc, size_comp;
245 size_t bytes;
246 int err;
247
248 in_fd = do_file(inf, &size_comp, O_RDONLY);
249 if (in_fd < 0) {
250 printf("\tError opening input file %s\n", inf);
251 err = 1;
252 goto out;
253 }
254
255 out_fd = do_file(outf, 0, O_CREAT | O_WRONLY);
256 if (out_fd < 0) {
257 printf("\tError opening output file %s\n", outf);
258 err = 1;
259 goto out_close_in;
260 }
261
262 in_buf = calloc(size_comp, 1);
263 if (!in_buf) {
264 printf("\tError allocating 0x%zx bytes for input buffer\n", size_comp);
265 err = 1;
266 goto out_close_out;
267 }
268
269 bytes = read(in_fd, in_buf, size_comp);
270 if (bytes != size_comp) {
271 printf("\tError reading input file %s\n", inf);
272 err = 1;
273 goto out_free_in;
274 }
275
276 size_comp = ((header *)in_buf)->size;
277
278 size_unc = max_size ? max_size : UNCOMP_MAX;
279 out_buf = calloc(size_unc, 1);
280 if (!out_buf) {
281 printf("\tError allocating 0x%zx bytes for output buffer\n", size_unc);
282 err = 1;
283 goto out_free_in;
284 }
285
286 err = uncompress((Bytef *)out_buf, &size_unc,
287 (Bytef *)in_buf + sizeof(header), size_comp);
288 if (err != Z_OK) {
289 printf("\tzlib uncompression error %d\n", err);
290 err = 1;
291 goto out_free_out;
292 }
293
294 if (DEBUG_FILE)
295 printf("Uncompressed 0x%zx bytes into 0x%zx\n", size_comp, size_unc);
296
297 bytes = write(out_fd, out_buf, size_unc);
298 if (bytes != size_unc) {
299 printf("\tError writing to %s\n", outf);
300 err = 1;
301 /* fall through to out_free_out */
302 }
303
304out_free_out:
305 free(out_buf);
306out_free_in:
307 free(in_buf);
308out_close_out:
309 close(out_fd);
310out_close_in:
311 close(in_fd);
312out:
313 if (err)
314 exit(err);
315}
316
317int main(int argc, char *argv[])
318{
319 int c;
320 char *inf = 0, *outf = 0, *scratch;
321 int direction = DIR_UNDEF;
322 size_t max_size = 0;
323
324 while (1) {
325 int optindex = 0;
326
327 c = getopt_long(argc, argv, optstring, long_options, &optindex);
328 if (c == -1)
329 break;
330
331 switch (c) {
332 case 'i':
333 inf = optarg;
334 break;
335 case 'o':
336 outf = optarg;
337 break;
338 case 'c':
339 if (direction != DIR_UNDEF)
340 usage();
341 direction = DIR_COMP;
342 break;
343 case 'u':
344 if (direction != DIR_UNDEF)
345 usage();
346 direction = DIR_UNCOMP;
347 break;
348 case 'm':
349 max_size = strtoull(optarg, &scratch, 16);
350 break;
351 case 'h':
352 usage();
353 }
354 }
355 if (!inf || !outf || direction == DIR_UNDEF)
356 usage();
357
358 if (direction == DIR_COMP)
359 do_compress(outf, inf, max_size);
360 else
361 do_uncompress(outf, inf, max_size);
362
363 return 0;
364}