Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd
  4 *
  5 * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
  6 */
  7
  8#ifdef STATIC
  9#define PREBOOT
 10#include "lz4/lz4_decompress.c"
 11#else
 12#include <linux/decompress/unlz4.h>
 13#endif
 14#include <linux/types.h>
 15#include <linux/lz4.h>
 16#include <linux/decompress/mm.h>
 17#include <linux/compiler.h>
 18
 19#include <asm/unaligned.h>
 20
 21/*
 22 * Note: Uncompressed chunk size is used in the compressor side
 23 * (userspace side for compression).
 24 * It is hardcoded because there is not proper way to extract it
 25 * from the binary stream which is generated by the preliminary
 26 * version of LZ4 tool so far.
 27 */
 28#define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20)
 29#define ARCHIVE_MAGICNUMBER 0x184C2102
 30
 31STATIC inline int INIT unlz4(u8 *input, long in_len,
 32				long (*fill)(void *, unsigned long),
 33				long (*flush)(void *, unsigned long),
 34				u8 *output, long *posp,
 35				void (*error) (char *x))
 36{
 37	int ret = -1;
 38	size_t chunksize = 0;
 39	size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE;
 40	u8 *inp;
 41	u8 *inp_start;
 42	u8 *outp;
 43	long size = in_len;
 44#ifdef PREBOOT
 45	size_t out_len = get_unaligned_le32(input + in_len);
 46#endif
 47	size_t dest_len;
 48
 49
 50	if (output) {
 51		outp = output;
 52	} else if (!flush) {
 53		error("NULL output pointer and no flush function provided");
 54		goto exit_0;
 55	} else {
 56		outp = large_malloc(uncomp_chunksize);
 57		if (!outp) {
 58			error("Could not allocate output buffer");
 59			goto exit_0;
 60		}
 61	}
 62
 63	if (input && fill) {
 64		error("Both input pointer and fill function provided,");
 65		goto exit_1;
 66	} else if (input) {
 67		inp = input;
 68	} else if (!fill) {
 69		error("NULL input pointer and missing fill function");
 70		goto exit_1;
 71	} else {
 72		inp = large_malloc(LZ4_compressBound(uncomp_chunksize));
 73		if (!inp) {
 74			error("Could not allocate input buffer");
 75			goto exit_1;
 76		}
 77	}
 78	inp_start = inp;
 79
 80	if (posp)
 81		*posp = 0;
 82
 83	if (fill) {
 84		size = fill(inp, 4);
 85		if (size < 4) {
 86			error("data corrupted");
 87			goto exit_2;
 88		}
 89	}
 90
 91	chunksize = get_unaligned_le32(inp);
 92	if (chunksize == ARCHIVE_MAGICNUMBER) {
 93		if (!fill) {
 94			inp += 4;
 95			size -= 4;
 96		}
 97	} else {
 98		error("invalid header");
 99		goto exit_2;
100	}
101
102	if (posp)
103		*posp += 4;
104
105	for (;;) {
106
107		if (fill) {
108			size = fill(inp, 4);
109			if (size == 0)
110				break;
111			if (size < 4) {
112				error("data corrupted");
113				goto exit_2;
114			}
115		} else if (size < 4) {
116			/* empty or end-of-file */
117			goto exit_3;
118		}
119
120		chunksize = get_unaligned_le32(inp);
121		if (chunksize == ARCHIVE_MAGICNUMBER) {
122			if (!fill) {
123				inp += 4;
124				size -= 4;
125			}
126			if (posp)
127				*posp += 4;
128			continue;
129		}
130
131		if (!fill && chunksize == 0) {
132			/* empty or end-of-file */
133			goto exit_3;
134		}
135
136		if (posp)
137			*posp += 4;
138
139		if (!fill) {
140			inp += 4;
141			size -= 4;
142		} else {
143			if (chunksize > LZ4_compressBound(uncomp_chunksize)) {
144				error("chunk length is longer than allocated");
145				goto exit_2;
146			}
147			size = fill(inp, chunksize);
148			if (size < chunksize) {
149				error("data corrupted");
150				goto exit_2;
151			}
152		}
153#ifdef PREBOOT
154		if (out_len >= uncomp_chunksize) {
155			dest_len = uncomp_chunksize;
156			out_len -= dest_len;
157		} else
158			dest_len = out_len;
159
160		ret = LZ4_decompress_fast(inp, outp, dest_len);
161		chunksize = ret;
162#else
163		dest_len = uncomp_chunksize;
164
165		ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len);
166		dest_len = ret;
167#endif
168		if (ret < 0) {
169			error("Decoding failed");
170			goto exit_2;
171		}
172
173		ret = -1;
174		if (flush && flush(outp, dest_len) != dest_len)
175			goto exit_2;
176		if (output)
177			outp += dest_len;
178		if (posp)
179			*posp += chunksize;
180
181		if (!fill) {
182			size -= chunksize;
183
184			if (size == 0)
185				break;
186			else if (size < 0) {
187				error("data corrupted");
188				goto exit_2;
189			}
190			inp += chunksize;
191		}
192	}
193
194exit_3:
195	ret = 0;
196exit_2:
197	if (!input)
198		large_free(inp_start);
199exit_1:
200	if (!output)
201		large_free(outp);
202exit_0:
203	return ret;
204}
205
206#ifdef PREBOOT
207STATIC int INIT __decompress(unsigned char *buf, long in_len,
208			      long (*fill)(void*, unsigned long),
209			      long (*flush)(void*, unsigned long),
210			      unsigned char *output, long out_len,
211			      long *posp,
212			      void (*error)(char *x)
213	)
214{
215	return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
216}
217#endif