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, int in_len,
  35                                int (*fill) (void *, unsigned int),
  36                                int (*flush) (void *, unsigned int),
  37                                u8 *output, int *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        int 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                fill(inp, 4);
  88
  89        chunksize = get_unaligned_le32(inp);
  90        if (chunksize == ARCHIVE_MAGICNUMBER) {
  91                inp += 4;
  92                size -= 4;
  93        } else {
  94                error("invalid header");
  95                goto exit_2;
  96        }
  97
  98        if (posp)
  99                *posp += 4;
 100
 101        for (;;) {
 102
 103                if (fill)
 104                        fill(inp, 4);
 105
 106                chunksize = get_unaligned_le32(inp);
 107                if (chunksize == ARCHIVE_MAGICNUMBER) {
 108                        inp += 4;
 109                        size -= 4;
 110                        if (posp)
 111                                *posp += 4;
 112                        continue;
 113                }
 114                inp += 4;
 115                size -= 4;
 116
 117                if (posp)
 118                        *posp += 4;
 119
 120                if (fill) {
 121                        if (chunksize > lz4_compressbound(uncomp_chunksize)) {
 122                                error("chunk length is longer than allocated");
 123                                goto exit_2;
 124                        }
 125                        fill(inp, chunksize);
 126                }
 127#ifdef PREBOOT
 128                if (out_len >= uncomp_chunksize) {
 129                        dest_len = uncomp_chunksize;
 130                        out_len -= dest_len;
 131                } else
 132                        dest_len = out_len;
 133                ret = lz4_decompress(inp, &chunksize, outp, dest_len);
 134#else
 135                dest_len = uncomp_chunksize;
 136                ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp,
 137                                &dest_len);
 138#endif
 139                if (ret < 0) {
 140                        error("Decoding failed");
 141                        goto exit_2;
 142                }
 143
 144                if (flush && flush(outp, dest_len) != dest_len)
 145                        goto exit_2;
 146                if (output)
 147                        outp += dest_len;
 148                if (posp)
 149                        *posp += chunksize;
 150
 151                size -= chunksize;
 152
 153                if (size == 0)
 154                        break;
 155                else if (size < 0) {
 156                        error("data corrupted");
 157                        goto exit_2;
 158                }
 159
 160                inp += chunksize;
 161                if (fill)
 162                        inp = inp_start;
 163        }
 164
 165        ret = 0;
 166exit_2:
 167        if (!input)
 168                large_free(inp_start);
 169exit_1:
 170        if (!output)
 171                large_free(outp);
 172exit_0:
 173        return ret;
 174}
 175
 176#ifdef PREBOOT
 177STATIC int INIT decompress(unsigned char *buf, int in_len,
 178                              int(*fill)(void*, unsigned int),
 179                              int(*flush)(void*, unsigned int),
 180                              unsigned char *output,
 181                              int *posp,
 182                              void(*error)(char *x)
 183        )
 184{
 185        return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
 186}
 187#endif
 188