xref: /openbmc/u-boot/test/compression.c (revision 867a6ac8)
1 /*
2  * Copyright (c) 2013, The Chromium Authors
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #define DEBUG
8 
9 #include <common.h>
10 #include <bootm.h>
11 #include <command.h>
12 #include <malloc.h>
13 #include <mapmem.h>
14 #include <asm/io.h>
15 
16 #include <u-boot/zlib.h>
17 #include <bzlib.h>
18 
19 #include <lzma/LzmaTypes.h>
20 #include <lzma/LzmaDec.h>
21 #include <lzma/LzmaTools.h>
22 
23 #include <linux/lzo.h>
24 
25 static const char plain[] =
26 	"I am a highly compressable bit of text.\n"
27 	"I am a highly compressable bit of text.\n"
28 	"I am a highly compressable bit of text.\n"
29 	"There are many like me, but this one is mine.\n"
30 	"If I were any shorter, there wouldn't be much sense in\n"
31 	"compressing me in the first place. At least with lzo, anyway,\n"
32 	"which appears to behave poorly in the face of short text\n"
33 	"messages.\n";
34 
35 /* bzip2 -c /tmp/plain.txt > /tmp/plain.bz2 */
36 static const char bzip2_compressed[] =
37 	"\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\xe5\x63\xdd\x09\x00\x00"
38 	"\x28\x57\x80\x00\x10\x40\x85\x20\x20\x04\x00\x3f\xef\xdf\xf0\x30"
39 	"\x00\xd6\xd0\x34\x91\x89\xa6\xf5\x4d\x19\x1a\x19\x0d\x02\x34\xd4"
40 	"\xc9\x00\x34\x34\x00\x02\x48\x41\x35\x4f\xd4\xc6\x88\xd3\x50\x3d"
41 	"\x4f\x51\x82\x4f\x88\xc3\x0d\x05\x62\x4f\x91\xa3\x52\x1b\xd0\x52"
42 	"\x41\x4a\xa3\x98\xc2\x6b\xca\xa3\x82\xa5\xac\x8b\x15\x99\x68\xad"
43 	"\xdf\x29\xd6\xf1\xf7\x5a\x10\xcd\x8c\x26\x61\x94\x95\xfe\x9e\x16"
44 	"\x18\x28\x69\xd4\x23\x64\xcc\x2b\xe5\xe8\x5f\x00\xa4\x70\x26\x2c"
45 	"\xee\xbd\x59\x6d\x6a\xec\xfc\x31\xda\x59\x0a\x14\x2a\x60\x1c\xf0"
46 	"\x04\x86\x73\x9a\xc5\x5b\x87\x3f\x5b\x4c\x93\xe6\xb5\x35\x0d\xa6"
47 	"\xb1\x2e\x62\x7b\xab\x67\xe7\x99\x2a\x14\x5e\x9f\x64\xcb\x96\xf4"
48 	"\x0d\x65\xd4\x39\xe6\x8b\x7e\xea\x1c\x03\x69\x97\x83\x58\x91\x96"
49 	"\xe1\xf0\x9d\xa4\x15\x8b\xb8\xc6\x93\xdc\x3d\xd9\x3c\x22\x55\xef"
50 	"\xfb\xbb\x2a\xd3\x87\xa2\x8b\x04\xd9\x19\xf8\xe2\xfd\x4f\xdb\x1a"
51 	"\x07\xc8\x60\xa3\x3f\xf8\xbb\x92\x29\xc2\x84\x87\x2b\x1e\xe8\x48";
52 static const unsigned long bzip2_compressed_size = 240;
53 
54 /* lzma -z -c /tmp/plain.txt > /tmp/plain.lzma */
55 static const char lzma_compressed[] =
56 	"\x5d\x00\x00\x80\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x24\x88"
57 	"\x08\x26\xd8\x41\xff\x99\xc8\xcf\x66\x3d\x80\xac\xba\x17\xf1\xc8"
58 	"\xb9\xdf\x49\x37\xb1\x68\xa0\x2a\xdd\x63\xd1\xa7\xa3\x66\xf8\x15"
59 	"\xef\xa6\x67\x8a\x14\x18\x80\xcb\xc7\xb1\xcb\x84\x6a\xb2\x51\x16"
60 	"\xa1\x45\xa0\xd6\x3e\x55\x44\x8a\x5c\xa0\x7c\xe5\xa8\xbd\x04\x57"
61 	"\x8f\x24\xfd\xb9\x34\x50\x83\x2f\xf3\x46\x3e\xb9\xb0\x00\x1a\xf5"
62 	"\xd3\x86\x7e\x8f\x77\xd1\x5d\x0e\x7c\xe1\xac\xde\xf8\x65\x1f\x4d"
63 	"\xce\x7f\xa7\x3d\xaa\xcf\x26\xa7\x58\x69\x1e\x4c\xea\x68\x8a\xe5"
64 	"\x89\xd1\xdc\x4d\xc7\xe0\x07\x42\xbf\x0c\x9d\x06\xd7\x51\xa2\x0b"
65 	"\x7c\x83\x35\xe1\x85\xdf\xee\xfb\xa3\xee\x2f\x47\x5f\x8b\x70\x2b"
66 	"\xe1\x37\xf3\x16\xf6\x27\x54\x8a\x33\x72\x49\xea\x53\x7d\x60\x0b"
67 	"\x21\x90\x66\xe7\x9e\x56\x61\x5d\xd8\xdc\x59\xf0\xac\x2f\xd6\x49"
68 	"\x6b\x85\x40\x08\x1f\xdf\x26\x25\x3b\x72\x44\xb0\xb8\x21\x2f\xb3"
69 	"\xd7\x9b\x24\x30\x78\x26\x44\x07\xc3\x33\xd1\x4d\x03\x1b\xe1\xff"
70 	"\xfd\xf5\x50\x8d\xca";
71 static const unsigned long lzma_compressed_size = 229;
72 
73 /* lzop -c /tmp/plain.txt > /tmp/plain.lzo */
74 static const char lzo_compressed[] =
75 	"\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a\x10\x30\x20\x60\x09\x40\x01"
76 	"\x05\x03\x00\x00\x09\x00\x00\x81\xb4\x52\x09\x54\xf1\x00\x00\x00"
77 	"\x00\x09\x70\x6c\x61\x69\x6e\x2e\x74\x78\x74\x65\xb1\x07\x9c\x00"
78 	"\x00\x01\x5e\x00\x00\x01\x0f\xc3\xc7\x7a\xe0\x00\x16\x49\x20\x61"
79 	"\x6d\x20\x61\x20\x68\x69\x67\x68\x6c\x79\x20\x63\x6f\x6d\x70\x72"
80 	"\x65\x73\x73\x61\x62\x6c\x65\x20\x62\x69\x74\x20\x6f\x66\x20\x74"
81 	"\x65\x78\x74\x2e\x0a\x20\x2f\x9c\x00\x00\x22\x54\x68\x65\x72\x65"
82 	"\x20\x61\x72\x65\x20\x6d\x61\x6e\x79\x20\x6c\x69\x6b\x65\x20\x6d"
83 	"\x65\x2c\x20\x62\x75\x74\x20\x74\x68\x69\x73\x20\x6f\x6e\x65\x20"
84 	"\x69\x73\x20\x6d\x69\x6e\x65\x2e\x0a\x49\x66\x20\x49\x20\x77\x84"
85 	"\x06\x0a\x6e\x79\x20\x73\x68\x6f\x72\x74\x65\x72\x2c\x20\x74\x90"
86 	"\x08\x00\x08\x77\x6f\x75\x6c\x64\x6e\x27\x74\x20\x62\x65\x20\x6d"
87 	"\x75\x63\x68\x20\x73\x65\x6e\x73\x65\x20\x69\x6e\x0a\xf8\x19\x02"
88 	"\x69\x6e\x67\x20\x6d\x64\x02\x64\x06\x00\x5a\x20\x66\x69\x72\x73"
89 	"\x74\x20\x70\x6c\x61\x63\x65\x2e\x20\x41\x74\x20\x6c\x65\x61\x73"
90 	"\x74\x20\x77\x69\x74\x68\x20\x6c\x7a\x6f\x2c\x20\x61\x6e\x79\x77"
91 	"\x61\x79\x2c\x0a\x77\x68\x69\x63\x68\x20\x61\x70\x70\x65\x61\x72"
92 	"\x73\x20\x74\x6f\x20\x62\x65\x68\x61\x76\x65\x20\x70\x6f\x6f\x72"
93 	"\x6c\x79\x20\x69\x6e\x20\x74\x68\x65\x20\x66\x61\x63\x65\x20\x6f"
94 	"\x66\x20\x73\x68\x6f\x72\x74\x20\x74\x65\x78\x74\x0a\x6d\x65\x73"
95 	"\x73\x61\x67\x65\x73\x2e\x0a\x11\x00\x00\x00\x00\x00\x00";
96 static const unsigned long lzo_compressed_size = 334;
97 
98 
99 #define TEST_BUFFER_SIZE	512
100 
101 typedef int (*mutate_func)(void *, unsigned long, void *, unsigned long,
102 			   unsigned long *);
103 
104 static int compress_using_gzip(void *in, unsigned long in_size,
105 			       void *out, unsigned long out_max,
106 			       unsigned long *out_size)
107 {
108 	int ret;
109 	unsigned long inout_size = out_max;
110 
111 	ret = gzip(out, &inout_size, in, in_size);
112 	if (out_size)
113 		*out_size = inout_size;
114 
115 	return ret;
116 }
117 
118 static int uncompress_using_gzip(void *in, unsigned long in_size,
119 				 void *out, unsigned long out_max,
120 				 unsigned long *out_size)
121 {
122 	int ret;
123 	unsigned long inout_size = in_size;
124 
125 	ret = gunzip(out, out_max, in, &inout_size);
126 	if (out_size)
127 		*out_size = inout_size;
128 
129 	return ret;
130 }
131 
132 static int compress_using_bzip2(void *in, unsigned long in_size,
133 				void *out, unsigned long out_max,
134 				unsigned long *out_size)
135 {
136 	/* There is no bzip2 compression in u-boot, so fake it. */
137 	assert(in_size == strlen(plain));
138 	assert(memcmp(plain, in, in_size) == 0);
139 
140 	if (bzip2_compressed_size > out_max)
141 		return -1;
142 
143 	memcpy(out, bzip2_compressed, bzip2_compressed_size);
144 	if (out_size)
145 		*out_size = bzip2_compressed_size;
146 
147 	return 0;
148 }
149 
150 static int uncompress_using_bzip2(void *in, unsigned long in_size,
151 				  void *out, unsigned long out_max,
152 				  unsigned long *out_size)
153 {
154 	int ret;
155 	unsigned int inout_size = out_max;
156 
157 	ret = BZ2_bzBuffToBuffDecompress(out, &inout_size, in, in_size,
158 			CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0);
159 	if (out_size)
160 		*out_size = inout_size;
161 
162 	return (ret != BZ_OK);
163 }
164 
165 static int compress_using_lzma(void *in, unsigned long in_size,
166 			       void *out, unsigned long out_max,
167 			       unsigned long *out_size)
168 {
169 	/* There is no lzma compression in u-boot, so fake it. */
170 	assert(in_size == strlen(plain));
171 	assert(memcmp(plain, in, in_size) == 0);
172 
173 	if (lzma_compressed_size > out_max)
174 		return -1;
175 
176 	memcpy(out, lzma_compressed, lzma_compressed_size);
177 	if (out_size)
178 		*out_size = lzma_compressed_size;
179 
180 	return 0;
181 }
182 
183 static int uncompress_using_lzma(void *in, unsigned long in_size,
184 				 void *out, unsigned long out_max,
185 				 unsigned long *out_size)
186 {
187 	int ret;
188 	SizeT inout_size = out_max;
189 
190 	ret = lzmaBuffToBuffDecompress(out, &inout_size, in, in_size);
191 	if (out_size)
192 		*out_size = inout_size;
193 
194 	return (ret != SZ_OK);
195 }
196 
197 static int compress_using_lzo(void *in, unsigned long in_size,
198 			      void *out, unsigned long out_max,
199 			      unsigned long *out_size)
200 {
201 	/* There is no lzo compression in u-boot, so fake it. */
202 	assert(in_size == strlen(plain));
203 	assert(memcmp(plain, in, in_size) == 0);
204 
205 	if (lzo_compressed_size > out_max)
206 		return -1;
207 
208 	memcpy(out, lzo_compressed, lzo_compressed_size);
209 	if (out_size)
210 		*out_size = lzo_compressed_size;
211 
212 	return 0;
213 }
214 
215 static int uncompress_using_lzo(void *in, unsigned long in_size,
216 				void *out, unsigned long out_max,
217 				unsigned long *out_size)
218 {
219 	int ret;
220 	size_t input_size = in_size;
221 	size_t output_size = out_max;
222 
223 	ret = lzop_decompress(in, input_size, out, &output_size);
224 	if (out_size)
225 		*out_size = output_size;
226 
227 	return (ret != LZO_E_OK);
228 }
229 
230 #define errcheck(statement) if (!(statement)) { \
231 	fprintf(stderr, "\tFailed: %s\n", #statement); \
232 	ret = 1; \
233 	goto out; \
234 }
235 
236 static int run_test(char *name, mutate_func compress, mutate_func uncompress)
237 {
238 	ulong orig_size, compressed_size, uncompressed_size;
239 	void *orig_buf;
240 	void *compressed_buf = NULL;
241 	void *uncompressed_buf = NULL;
242 	void *compare_buf = NULL;
243 	int ret;
244 
245 	printf(" testing %s ...\n", name);
246 
247 	orig_buf = (void *)plain;
248 	orig_size = strlen(orig_buf); /* Trailing NULL not included. */
249 	errcheck(orig_size > 0);
250 
251 	compressed_size = uncompressed_size = TEST_BUFFER_SIZE;
252 	compressed_buf = malloc(compressed_size);
253 	errcheck(compressed_buf != NULL);
254 	uncompressed_buf = malloc(uncompressed_size);
255 	errcheck(uncompressed_buf != NULL);
256 	compare_buf = malloc(uncompressed_size);
257 	errcheck(compare_buf != NULL);
258 
259 	/* Compress works as expected. */
260 	printf("\torig_size:%lu\n", orig_size);
261 	memset(compressed_buf, 'A', TEST_BUFFER_SIZE);
262 	errcheck(compress(orig_buf, orig_size,
263 			compressed_buf, compressed_size,
264 			&compressed_size) == 0);
265 	printf("\tcompressed_size:%lu\n", compressed_size);
266 	errcheck(compressed_size > 0);
267 	errcheck(compressed_size < orig_size);
268 	errcheck(((char *)compressed_buf)[compressed_size-1] != 'A');
269 	errcheck(((char *)compressed_buf)[compressed_size] == 'A');
270 
271 	/* Uncompresses with space remaining. */
272 	errcheck(uncompress(compressed_buf, compressed_size,
273 			  uncompressed_buf, uncompressed_size,
274 			  &uncompressed_size) == 0);
275 	printf("\tuncompressed_size:%lu\n", uncompressed_size);
276 	errcheck(uncompressed_size == orig_size);
277 	errcheck(memcmp(orig_buf, uncompressed_buf, orig_size) == 0);
278 
279 	/* Uncompresses with exactly the right size output buffer. */
280 	memset(uncompressed_buf, 'A', TEST_BUFFER_SIZE);
281 	errcheck(uncompress(compressed_buf, compressed_size,
282 			  uncompressed_buf, orig_size,
283 			  &uncompressed_size) == 0);
284 	errcheck(uncompressed_size == orig_size);
285 	errcheck(memcmp(orig_buf, uncompressed_buf, orig_size) == 0);
286 	errcheck(((char *)uncompressed_buf)[orig_size] == 'A');
287 
288 	/* Make sure compression does not over-run. */
289 	memset(compare_buf, 'A', TEST_BUFFER_SIZE);
290 	ret = compress(orig_buf, orig_size,
291 		       compare_buf, compressed_size - 1,
292 		       NULL);
293 	errcheck(((char *)compare_buf)[compressed_size] == 'A');
294 	errcheck(ret != 0);
295 	printf("\tcompress does not overrun\n");
296 
297 	/* Make sure decompression does not over-run. */
298 	memset(compare_buf, 'A', TEST_BUFFER_SIZE);
299 	ret = uncompress(compressed_buf, compressed_size,
300 			 compare_buf, uncompressed_size - 1,
301 			 NULL);
302 	errcheck(((char *)compare_buf)[uncompressed_size - 1] == 'A');
303 	errcheck(ret != 0);
304 	printf("\tuncompress does not overrun\n");
305 
306 	/* Got here, everything is fine. */
307 	ret = 0;
308 
309 out:
310 	printf(" %s: %s\n", name, ret == 0 ? "ok" : "FAILED");
311 
312 	free(compare_buf);
313 	free(uncompressed_buf);
314 	free(compressed_buf);
315 
316 	return ret;
317 }
318 
319 static int do_ut_compression(cmd_tbl_t *cmdtp, int flag, int argc,
320 			     char *const argv[])
321 {
322 	int err = 0;
323 
324 	err += run_test("gzip", compress_using_gzip, uncompress_using_gzip);
325 	err += run_test("bzip2", compress_using_bzip2, uncompress_using_bzip2);
326 	err += run_test("lzma", compress_using_lzma, uncompress_using_lzma);
327 	err += run_test("lzo", compress_using_lzo, uncompress_using_lzo);
328 
329 	printf("ut_compression %s\n", err == 0 ? "ok" : "FAILED");
330 
331 	return err;
332 }
333 
334 static int compress_using_none(void *in, unsigned long in_size,
335 			       void *out, unsigned long out_max,
336 			       unsigned long *out_size)
337 {
338 	/* Here we just copy */
339 	memcpy(out, in, in_size);
340 	*out_size = in_size;
341 
342 	return 0;
343 }
344 
345 /**
346  * run_bootm_test() - Run tests on the bootm decopmression function
347  *
348  * @comp_type:	Compression type to test
349  * @compress:	Our function to compress data
350  * @return 0 if OK, non-zero on failure
351  */
352 static int run_bootm_test(int comp_type, mutate_func compress)
353 {
354 	ulong compress_size = 1024;
355 	void *compress_buff;
356 	int unc_len;
357 	int err = 0;
358 	const ulong image_start = 0;
359 	const ulong load_addr = 0x1000;
360 	ulong load_end;
361 
362 	printf("Testing: %s\n", genimg_get_comp_name(comp_type));
363 	compress_buff = map_sysmem(image_start, 0);
364 	unc_len = strlen(plain);
365 	compress((void *)plain, unc_len, compress_buff, compress_size,
366 		 &compress_size);
367 	err = bootm_decomp_image(comp_type, load_addr, image_start,
368 				 IH_TYPE_KERNEL, map_sysmem(load_addr, 0),
369 				 compress_buff, compress_size, unc_len,
370 				 &load_end);
371 	if (err)
372 		return err;
373 	err = bootm_decomp_image(comp_type, load_addr, image_start,
374 				 IH_TYPE_KERNEL, map_sysmem(load_addr, 0),
375 				 compress_buff, compress_size, unc_len - 1,
376 				 &load_end);
377 	if (!err)
378 		return -EINVAL;
379 
380 	/* We can't detect corruption when not decompressing */
381 	if (comp_type == IH_COMP_NONE)
382 		return 0;
383 	memset(compress_buff + compress_size / 2, '\x49',
384 	       compress_size / 2);
385 	err = bootm_decomp_image(comp_type, load_addr, image_start,
386 				 IH_TYPE_KERNEL, map_sysmem(load_addr, 0),
387 				 compress_buff, compress_size, 0x10000,
388 				 &load_end);
389 	if (!err)
390 		return -EINVAL;
391 
392 	return 0;
393 }
394 
395 static int do_ut_image_decomp(cmd_tbl_t *cmdtp, int flag, int argc,
396 			      char *const argv[])
397 {
398 	int err = 0;
399 
400 	err = run_bootm_test(IH_COMP_GZIP, compress_using_gzip);
401 	err |= run_bootm_test(IH_COMP_BZIP2, compress_using_bzip2);
402 	err |= run_bootm_test(IH_COMP_LZMA, compress_using_lzma);
403 	err |= run_bootm_test(IH_COMP_LZO, compress_using_lzo);
404 	err |= run_bootm_test(IH_COMP_NONE, compress_using_none);
405 
406 	printf("ut_image_decomp %s\n", err == 0 ? "ok" : "FAILED");
407 
408 	return 0;
409 }
410 
411 U_BOOT_CMD(
412 	ut_compression,	5,	1,	do_ut_compression,
413 	"Basic test of compressors: gzip bzip2 lzma lzo", ""
414 );
415 
416 U_BOOT_CMD(
417 	ut_image_decomp,	5,	1, do_ut_image_decomp,
418 	"Basic test of bootm decompression", ""
419 );
420