xref: /aosp_15_r20/external/coreboot/util/cbfstool/amdcompress.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* AMD Family 17h and later BIOS compressor */
2 /* SPDX-License-Identifier: GPL-2.0-only */
3 
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 
21 typedef struct _header {
22 	uint32_t rsvd1[5];
23 	uint32_t size;
24 	uint32_t rsvd2[58];
25 }  __attribute__((packed)) header;
26 
27 static const char *optstring  = "i:o:cm:uh";
28 
29 static struct option long_options[] = {
30 	{"infile",           required_argument, 0, 'i' },
31 	{"outfile",          required_argument, 0, 'o' },
32 	{"compress",         no_argument,       0, 'c' },
33 	{"maxsize",          required_argument, 0, 'm' },
34 	{"uncompress",       no_argument,       0, 'u' },
35 	{"help",             no_argument,       0, 'h' },
36 };
37 
usage(void)38 static 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 
do_file(char * name,size_t * size,int oflag)58 static 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 
parse_elf_to_xip_ram(const struct buffer * input,struct buffer * output)77 static 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 
convert_elf(struct buffer * buf)92 static 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 
iself(const void * input)109 static 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  */
do_compress(char * outf,char * inf,size_t max_size)138 static 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 
215 out_free_out:
216 	free(outbf.data);
217 out_free_in:
218 	free(inbf.data);
219 out_close_out:
220 	close(out_fd);
221 out_close_in:
222 	close(in_fd);
223 out:
224 	if (err)
225 		exit(err);
226 }
227 
do_uncompress(char * outf,char * inf,size_t max_size)228 static 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 
292 out_free_out:
293 	free(out_buf);
294 out_free_in:
295 	free(in_buf);
296 out_close_out:
297 	close(out_fd);
298 out_close_in:
299 	close(in_fd);
300 out:
301 	if (err)
302 		exit(err);
303 }
304 
main(int argc,char * argv[])305 int 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 }
353