xref: /openbmc/u-boot/lib/gunzip.c (revision 72b7af5a04afc22913e943decc1733a25b8eefcf)
1  /*
2   * (C) Copyright 2000-2006
3   * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
4   *
5   * SPDX-License-Identifier:	GPL-2.0+
6   */
7  
8  #include <common.h>
9  #include <watchdog.h>
10  #include <command.h>
11  #include <console.h>
12  #include <image.h>
13  #include <malloc.h>
14  #include <memalign.h>
15  #include <u-boot/zlib.h>
16  #include <div64.h>
17  
18  #define HEADER0			'\x1f'
19  #define HEADER1			'\x8b'
20  #define	ZALLOC_ALIGNMENT	16
21  #define HEAD_CRC		2
22  #define EXTRA_FIELD		4
23  #define ORIG_NAME		8
24  #define COMMENT			0x10
25  #define RESERVED		0xe0
26  #define DEFLATED		8
27  
28  void *gzalloc(void *x, unsigned items, unsigned size)
29  {
30  	void *p;
31  
32  	size *= items;
33  	size = (size + ZALLOC_ALIGNMENT - 1) & ~(ZALLOC_ALIGNMENT - 1);
34  
35  	p = malloc (size);
36  
37  	return (p);
38  }
39  
40  void gzfree(void *x, void *addr, unsigned nb)
41  {
42  	free (addr);
43  }
44  
45  int gzip_parse_header(const unsigned char *src, unsigned long len)
46  {
47  	int i, flags;
48  
49  	/* skip header */
50  	i = 10;
51  	flags = src[3];
52  	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
53  		puts ("Error: Bad gzipped data\n");
54  		return (-1);
55  	}
56  	if ((flags & EXTRA_FIELD) != 0)
57  		i = 12 + src[10] + (src[11] << 8);
58  	if ((flags & ORIG_NAME) != 0)
59  		while (src[i++] != 0)
60  			;
61  	if ((flags & COMMENT) != 0)
62  		while (src[i++] != 0)
63  			;
64  	if ((flags & HEAD_CRC) != 0)
65  		i += 2;
66  	if (i >= len) {
67  		puts ("Error: gunzip out of data in header\n");
68  		return (-1);
69  	}
70  	return i;
71  }
72  
73  int gunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp)
74  {
75  	int offset = gzip_parse_header(src, *lenp);
76  
77  	if (offset < 0)
78  		return offset;
79  
80  	return zunzip(dst, dstlen, src, lenp, 1, offset);
81  }
82  
83  #ifdef CONFIG_CMD_UNZIP
84  __weak
85  void gzwrite_progress_init(u64 expectedsize)
86  {
87  	putc('\n');
88  }
89  
90  __weak
91  void gzwrite_progress(int iteration,
92  		     u64 bytes_written,
93  		     u64 total_bytes)
94  {
95  	if (0 == (iteration & 3))
96  		printf("%llu/%llu\r", bytes_written, total_bytes);
97  }
98  
99  __weak
100  void gzwrite_progress_finish(int returnval,
101  			     u64 bytes_written,
102  			     u64 total_bytes,
103  			     u32 expected_crc,
104  			     u32 calculated_crc)
105  {
106  	if (0 == returnval) {
107  		printf("\n\t%llu bytes, crc 0x%08x\n",
108  		       total_bytes, calculated_crc);
109  	} else {
110  		printf("\n\tuncompressed %llu of %llu\n"
111  		       "\tcrcs == 0x%08x/0x%08x\n",
112  		       bytes_written, total_bytes,
113  		       expected_crc, calculated_crc);
114  	}
115  }
116  
117  int gzwrite(unsigned char *src, int len,
118  	    struct blk_desc *dev,
119  	    unsigned long szwritebuf,
120  	    u64 startoffs,
121  	    u64 szexpected)
122  {
123  	int i, flags;
124  	z_stream s;
125  	int r = 0;
126  	unsigned char *writebuf;
127  	unsigned crc = 0;
128  	u64 totalfilled = 0;
129  	lbaint_t blksperbuf, outblock;
130  	u32 expected_crc;
131  	u32 payload_size;
132  	int iteration = 0;
133  
134  	if (!szwritebuf ||
135  	    (szwritebuf % dev->blksz) ||
136  	    (szwritebuf < dev->blksz)) {
137  		printf("%s: size %lu not a multiple of %lu\n",
138  		       __func__, szwritebuf, dev->blksz);
139  		return -1;
140  	}
141  
142  	if (startoffs & (dev->blksz-1)) {
143  		printf("%s: start offset %llu not a multiple of %lu\n",
144  		       __func__, startoffs, dev->blksz);
145  		return -1;
146  	}
147  
148  	blksperbuf = szwritebuf / dev->blksz;
149  	outblock = lldiv(startoffs, dev->blksz);
150  
151  	/* skip header */
152  	i = 10;
153  	flags = src[3];
154  	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
155  		puts("Error: Bad gzipped data\n");
156  		return -1;
157  	}
158  	if ((flags & EXTRA_FIELD) != 0)
159  		i = 12 + src[10] + (src[11] << 8);
160  	if ((flags & ORIG_NAME) != 0)
161  		while (src[i++] != 0)
162  			;
163  	if ((flags & COMMENT) != 0)
164  		while (src[i++] != 0)
165  			;
166  	if ((flags & HEAD_CRC) != 0)
167  		i += 2;
168  
169  	if (i >= len-8) {
170  		puts("Error: gunzip out of data in header");
171  		return -1;
172  	}
173  
174  	payload_size = len - i - 8;
175  
176  	memcpy(&expected_crc, src + len - 8, sizeof(expected_crc));
177  	expected_crc = le32_to_cpu(expected_crc);
178  	u32 szuncompressed;
179  	memcpy(&szuncompressed, src + len - 4, sizeof(szuncompressed));
180  	if (szexpected == 0) {
181  		szexpected = le32_to_cpu(szuncompressed);
182  	} else if (szuncompressed != (u32)szexpected) {
183  		printf("size of %llx doesn't match trailer low bits %x\n",
184  		       szexpected, szuncompressed);
185  		return -1;
186  	}
187  	if (lldiv(szexpected, dev->blksz) > (dev->lba - outblock)) {
188  		printf("%s: uncompressed size %llu exceeds device size\n",
189  		       __func__, szexpected);
190  		return -1;
191  	}
192  
193  	gzwrite_progress_init(szexpected);
194  
195  	s.zalloc = gzalloc;
196  	s.zfree = gzfree;
197  
198  	r = inflateInit2(&s, -MAX_WBITS);
199  	if (r != Z_OK) {
200  		printf("Error: inflateInit2() returned %d\n", r);
201  		return -1;
202  	}
203  
204  	s.next_in = src + i;
205  	s.avail_in = payload_size+8;
206  	writebuf = (unsigned char *)malloc_cache_aligned(szwritebuf);
207  
208  	/* decompress until deflate stream ends or end of file */
209  	do {
210  		if (s.avail_in == 0) {
211  			printf("%s: weird termination with result %d\n",
212  			       __func__, r);
213  			break;
214  		}
215  
216  		/* run inflate() on input until output buffer not full */
217  		do {
218  			unsigned long blocks_written;
219  			int numfilled;
220  			lbaint_t writeblocks;
221  
222  			s.avail_out = szwritebuf;
223  			s.next_out = writebuf;
224  			r = inflate(&s, Z_SYNC_FLUSH);
225  			if ((r != Z_OK) &&
226  			    (r != Z_STREAM_END)) {
227  				printf("Error: inflate() returned %d\n", r);
228  				goto out;
229  			}
230  			numfilled = szwritebuf - s.avail_out;
231  			crc = crc32(crc, writebuf, numfilled);
232  			totalfilled += numfilled;
233  			if (numfilled < szwritebuf) {
234  				writeblocks = (numfilled+dev->blksz-1)
235  						/ dev->blksz;
236  				memset(writebuf+numfilled, 0,
237  				       dev->blksz-(numfilled%dev->blksz));
238  			} else {
239  				writeblocks = blksperbuf;
240  			}
241  
242  			gzwrite_progress(iteration++,
243  					 totalfilled,
244  					 szexpected);
245  			blocks_written = blk_dwrite(dev, outblock,
246  						    writeblocks, writebuf);
247  			outblock += blocks_written;
248  			if (ctrlc()) {
249  				puts("abort\n");
250  				goto out;
251  			}
252  			WATCHDOG_RESET();
253  		} while (s.avail_out == 0);
254  		/* done when inflate() says it's done */
255  	} while (r != Z_STREAM_END);
256  
257  	if ((szexpected != totalfilled) ||
258  	    (crc != expected_crc))
259  		r = -1;
260  	else
261  		r = 0;
262  
263  out:
264  	gzwrite_progress_finish(r, totalfilled, szexpected,
265  				expected_crc, crc);
266  	free(writebuf);
267  	inflateEnd(&s);
268  
269  	return r;
270  }
271  #endif
272  
273  /*
274   * Uncompress blocks compressed with zlib without headers
275   */
276  int zunzip(void *dst, int dstlen, unsigned char *src, unsigned long *lenp,
277  						int stoponerr, int offset)
278  {
279  	z_stream s;
280  	int err = 0;
281  	int r;
282  
283  	s.zalloc = gzalloc;
284  	s.zfree = gzfree;
285  
286  	r = inflateInit2(&s, -MAX_WBITS);
287  	if (r != Z_OK) {
288  		printf("Error: inflateInit2() returned %d\n", r);
289  		return -1;
290  	}
291  	s.next_in = src + offset;
292  	s.avail_in = *lenp - offset;
293  	s.next_out = dst;
294  	s.avail_out = dstlen;
295  	do {
296  		r = inflate(&s, Z_FINISH);
297  		if (stoponerr == 1 && r != Z_STREAM_END &&
298  		    (s.avail_in == 0 || s.avail_out == 0 || r != Z_BUF_ERROR)) {
299  			printf("Error: inflate() returned %d\n", r);
300  			err = -1;
301  			break;
302  		}
303  	} while (r == Z_BUF_ERROR);
304  	*lenp = s.next_out - (unsigned char *) dst;
305  	inflateEnd(&s);
306  
307  	return err;
308  }
309