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