18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#ifdef STATIC
38c2ecf20Sopenharmony_ci#define PREBOOT
48c2ecf20Sopenharmony_ci/* Pre-boot environment: included */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci/* prevent inclusion of _LINUX_KERNEL_H in pre-boot environment: lots
78c2ecf20Sopenharmony_ci * errors about console_printk etc... on ARM */
88c2ecf20Sopenharmony_ci#define _LINUX_KERNEL_H
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "zlib_inflate/inftrees.c"
118c2ecf20Sopenharmony_ci#include "zlib_inflate/inffast.c"
128c2ecf20Sopenharmony_ci#include "zlib_inflate/inflate.c"
138c2ecf20Sopenharmony_ci#ifdef CONFIG_ZLIB_DFLTCC
148c2ecf20Sopenharmony_ci#include "zlib_dfltcc/dfltcc.c"
158c2ecf20Sopenharmony_ci#include "zlib_dfltcc/dfltcc_inflate.c"
168c2ecf20Sopenharmony_ci#endif
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#else /* STATIC */
198c2ecf20Sopenharmony_ci/* initramfs et al: linked */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/zutil.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "zlib_inflate/inftrees.h"
248c2ecf20Sopenharmony_ci#include "zlib_inflate/inffast.h"
258c2ecf20Sopenharmony_ci#include "zlib_inflate/inflate.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#include "zlib_inflate/infutil.h"
288c2ecf20Sopenharmony_ci#include <linux/decompress/inflate.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#endif /* STATIC */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#include <linux/decompress/mm.h>
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define GZIP_IOBUF_SIZE (16*1024)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic long INIT nofill(void *buffer, unsigned long len)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	return -1;
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* Included from initramfs et al code */
428c2ecf20Sopenharmony_ciSTATIC int INIT __gunzip(unsigned char *buf, long len,
438c2ecf20Sopenharmony_ci		       long (*fill)(void*, unsigned long),
448c2ecf20Sopenharmony_ci		       long (*flush)(void*, unsigned long),
458c2ecf20Sopenharmony_ci		       unsigned char *out_buf, long out_len,
468c2ecf20Sopenharmony_ci		       long *pos,
478c2ecf20Sopenharmony_ci		       void(*error)(char *x)) {
488c2ecf20Sopenharmony_ci	u8 *zbuf;
498c2ecf20Sopenharmony_ci	struct z_stream_s *strm;
508c2ecf20Sopenharmony_ci	int rc;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	rc = -1;
538c2ecf20Sopenharmony_ci	if (flush) {
548c2ecf20Sopenharmony_ci		out_len = 0x8000; /* 32 K */
558c2ecf20Sopenharmony_ci		out_buf = malloc(out_len);
568c2ecf20Sopenharmony_ci	} else {
578c2ecf20Sopenharmony_ci		if (!out_len)
588c2ecf20Sopenharmony_ci			out_len = ((size_t)~0) - (size_t)out_buf; /* no limit */
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci	if (!out_buf) {
618c2ecf20Sopenharmony_ci		error("Out of memory while allocating output buffer");
628c2ecf20Sopenharmony_ci		goto gunzip_nomem1;
638c2ecf20Sopenharmony_ci	}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (buf)
668c2ecf20Sopenharmony_ci		zbuf = buf;
678c2ecf20Sopenharmony_ci	else {
688c2ecf20Sopenharmony_ci		zbuf = malloc(GZIP_IOBUF_SIZE);
698c2ecf20Sopenharmony_ci		len = 0;
708c2ecf20Sopenharmony_ci	}
718c2ecf20Sopenharmony_ci	if (!zbuf) {
728c2ecf20Sopenharmony_ci		error("Out of memory while allocating input buffer");
738c2ecf20Sopenharmony_ci		goto gunzip_nomem2;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	strm = malloc(sizeof(*strm));
778c2ecf20Sopenharmony_ci	if (strm == NULL) {
788c2ecf20Sopenharmony_ci		error("Out of memory while allocating z_stream");
798c2ecf20Sopenharmony_ci		goto gunzip_nomem3;
808c2ecf20Sopenharmony_ci	}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	strm->workspace = malloc(flush ? zlib_inflate_workspacesize() :
838c2ecf20Sopenharmony_ci#ifdef CONFIG_ZLIB_DFLTCC
848c2ecf20Sopenharmony_ci	/* Always allocate the full workspace for DFLTCC */
858c2ecf20Sopenharmony_ci				 zlib_inflate_workspacesize());
868c2ecf20Sopenharmony_ci#else
878c2ecf20Sopenharmony_ci				 sizeof(struct inflate_state));
888c2ecf20Sopenharmony_ci#endif
898c2ecf20Sopenharmony_ci	if (strm->workspace == NULL) {
908c2ecf20Sopenharmony_ci		error("Out of memory while allocating workspace");
918c2ecf20Sopenharmony_ci		goto gunzip_nomem4;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (!fill)
958c2ecf20Sopenharmony_ci		fill = nofill;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (len == 0)
988c2ecf20Sopenharmony_ci		len = fill(zbuf, GZIP_IOBUF_SIZE);
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	/* verify the gzip header */
1018c2ecf20Sopenharmony_ci	if (len < 10 ||
1028c2ecf20Sopenharmony_ci	   zbuf[0] != 0x1f || zbuf[1] != 0x8b || zbuf[2] != 0x08) {
1038c2ecf20Sopenharmony_ci		if (pos)
1048c2ecf20Sopenharmony_ci			*pos = 0;
1058c2ecf20Sopenharmony_ci		error("Not a gzip file");
1068c2ecf20Sopenharmony_ci		goto gunzip_5;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* skip over gzip header (1f,8b,08... 10 bytes total +
1108c2ecf20Sopenharmony_ci	 * possible asciz filename)
1118c2ecf20Sopenharmony_ci	 */
1128c2ecf20Sopenharmony_ci	strm->next_in = zbuf + 10;
1138c2ecf20Sopenharmony_ci	strm->avail_in = len - 10;
1148c2ecf20Sopenharmony_ci	/* skip over asciz filename */
1158c2ecf20Sopenharmony_ci	if (zbuf[3] & 0x8) {
1168c2ecf20Sopenharmony_ci		do {
1178c2ecf20Sopenharmony_ci			/*
1188c2ecf20Sopenharmony_ci			 * If the filename doesn't fit into the buffer,
1198c2ecf20Sopenharmony_ci			 * the file is very probably corrupt. Don't try
1208c2ecf20Sopenharmony_ci			 * to read more data.
1218c2ecf20Sopenharmony_ci			 */
1228c2ecf20Sopenharmony_ci			if (strm->avail_in == 0) {
1238c2ecf20Sopenharmony_ci				error("header error");
1248c2ecf20Sopenharmony_ci				goto gunzip_5;
1258c2ecf20Sopenharmony_ci			}
1268c2ecf20Sopenharmony_ci			--strm->avail_in;
1278c2ecf20Sopenharmony_ci		} while (*strm->next_in++);
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	strm->next_out = out_buf;
1318c2ecf20Sopenharmony_ci	strm->avail_out = out_len;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	rc = zlib_inflateInit2(strm, -MAX_WBITS);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#ifdef CONFIG_ZLIB_DFLTCC
1368c2ecf20Sopenharmony_ci	/* Always keep the window for DFLTCC */
1378c2ecf20Sopenharmony_ci#else
1388c2ecf20Sopenharmony_ci	if (!flush) {
1398c2ecf20Sopenharmony_ci		WS(strm)->inflate_state.wsize = 0;
1408c2ecf20Sopenharmony_ci		WS(strm)->inflate_state.window = NULL;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci#endif
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	while (rc == Z_OK) {
1458c2ecf20Sopenharmony_ci		if (strm->avail_in == 0) {
1468c2ecf20Sopenharmony_ci			/* TODO: handle case where both pos and fill are set */
1478c2ecf20Sopenharmony_ci			len = fill(zbuf, GZIP_IOBUF_SIZE);
1488c2ecf20Sopenharmony_ci			if (len < 0) {
1498c2ecf20Sopenharmony_ci				rc = -1;
1508c2ecf20Sopenharmony_ci				error("read error");
1518c2ecf20Sopenharmony_ci				break;
1528c2ecf20Sopenharmony_ci			}
1538c2ecf20Sopenharmony_ci			strm->next_in = zbuf;
1548c2ecf20Sopenharmony_ci			strm->avail_in = len;
1558c2ecf20Sopenharmony_ci		}
1568c2ecf20Sopenharmony_ci		rc = zlib_inflate(strm, 0);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		/* Write any data generated */
1598c2ecf20Sopenharmony_ci		if (flush && strm->next_out > out_buf) {
1608c2ecf20Sopenharmony_ci			long l = strm->next_out - out_buf;
1618c2ecf20Sopenharmony_ci			if (l != flush(out_buf, l)) {
1628c2ecf20Sopenharmony_ci				rc = -1;
1638c2ecf20Sopenharmony_ci				error("write error");
1648c2ecf20Sopenharmony_ci				break;
1658c2ecf20Sopenharmony_ci			}
1668c2ecf20Sopenharmony_ci			strm->next_out = out_buf;
1678c2ecf20Sopenharmony_ci			strm->avail_out = out_len;
1688c2ecf20Sopenharmony_ci		}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci		/* after Z_FINISH, only Z_STREAM_END is "we unpacked it all" */
1718c2ecf20Sopenharmony_ci		if (rc == Z_STREAM_END) {
1728c2ecf20Sopenharmony_ci			rc = 0;
1738c2ecf20Sopenharmony_ci			break;
1748c2ecf20Sopenharmony_ci		} else if (rc != Z_OK) {
1758c2ecf20Sopenharmony_ci			error("uncompression error");
1768c2ecf20Sopenharmony_ci			rc = -1;
1778c2ecf20Sopenharmony_ci		}
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	zlib_inflateEnd(strm);
1818c2ecf20Sopenharmony_ci	if (pos)
1828c2ecf20Sopenharmony_ci		/* add + 8 to skip over trailer */
1838c2ecf20Sopenharmony_ci		*pos = strm->next_in - zbuf+8;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cigunzip_5:
1868c2ecf20Sopenharmony_ci	free(strm->workspace);
1878c2ecf20Sopenharmony_cigunzip_nomem4:
1888c2ecf20Sopenharmony_ci	free(strm);
1898c2ecf20Sopenharmony_cigunzip_nomem3:
1908c2ecf20Sopenharmony_ci	if (!buf)
1918c2ecf20Sopenharmony_ci		free(zbuf);
1928c2ecf20Sopenharmony_cigunzip_nomem2:
1938c2ecf20Sopenharmony_ci	if (flush)
1948c2ecf20Sopenharmony_ci		free(out_buf);
1958c2ecf20Sopenharmony_cigunzip_nomem1:
1968c2ecf20Sopenharmony_ci	return rc; /* returns Z_OK (0) if successful */
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci#ifndef PREBOOT
2008c2ecf20Sopenharmony_ciSTATIC int INIT gunzip(unsigned char *buf, long len,
2018c2ecf20Sopenharmony_ci		       long (*fill)(void*, unsigned long),
2028c2ecf20Sopenharmony_ci		       long (*flush)(void*, unsigned long),
2038c2ecf20Sopenharmony_ci		       unsigned char *out_buf,
2048c2ecf20Sopenharmony_ci		       long *pos,
2058c2ecf20Sopenharmony_ci		       void (*error)(char *x))
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	return __gunzip(buf, len, fill, flush, out_buf, 0, pos, error);
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci#else
2108c2ecf20Sopenharmony_ciSTATIC int INIT __decompress(unsigned char *buf, long len,
2118c2ecf20Sopenharmony_ci			   long (*fill)(void*, unsigned long),
2128c2ecf20Sopenharmony_ci			   long (*flush)(void*, unsigned long),
2138c2ecf20Sopenharmony_ci			   unsigned char *out_buf, long out_len,
2148c2ecf20Sopenharmony_ci			   long *pos,
2158c2ecf20Sopenharmony_ci			   void (*error)(char *x))
2168c2ecf20Sopenharmony_ci{
2178c2ecf20Sopenharmony_ci	return __gunzip(buf, len, fill, flush, out_buf, out_len, pos, error);
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci#endif
220