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