linux/lib/decompress_unlz4.c
<<
>>
Prefs
   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
 218