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