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