cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

decompress_unlz4.c (4277B)


      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