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