218 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd4 *5 * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>6 */7 8#ifdef STATIC9#define PREBOOT10#include "lz4/lz4_decompress.c"11#else12#include <linux/decompress/unlz4.h>13#endif14#include <linux/types.h>15#include <linux/lz4.h>16#include <linux/decompress/mm.h>17#include <linux/compiler.h>18 19#include <linux/unaligned.h>20 21/*22 * Note: Uncompressed chunk size is used in the compressor side23 * (userspace side for compression).24 * It is hardcoded because there is not proper way to extract it25 * from the binary stream which is generated by the preliminary26 * version of LZ4 tool so far.27 */28#define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20)29#define ARCHIVE_MAGICNUMBER 0x184C210230 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 PREBOOT45 size_t out_len = get_unaligned_le32(input + in_len);46#endif47 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 PREBOOT154 if (out_len >= uncomp_chunksize) {155 dest_len = uncomp_chunksize;156 out_len -= dest_len;157 } else158 dest_len = out_len;159 160 ret = LZ4_decompress_fast(inp, outp, dest_len);161 chunksize = ret;162#else163 dest_len = uncomp_chunksize;164 165 ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len);166 dest_len = ret;167#endif168 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 PREBOOT207STATIC 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#endif218