Back to home page

LXR

 
 

    


0001 /*
0002  * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd
0003  *
0004  * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
0005  *
0006  * This program is free software; you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License version 2 as
0008  * published by the Free Software Foundation.
0009  */
0010 
0011 #ifdef STATIC
0012 #define PREBOOT
0013 #include "lz4/lz4_decompress.c"
0014 #else
0015 #include <linux/decompress/unlz4.h>
0016 #endif
0017 #include <linux/types.h>
0018 #include <linux/lz4.h>
0019 #include <linux/decompress/mm.h>
0020 #include <linux/compiler.h>
0021 
0022 #include <asm/unaligned.h>
0023 
0024 /*
0025  * Note: Uncompressed chunk size is used in the compressor side
0026  * (userspace side for compression).
0027  * It is hardcoded because there is not proper way to extract it
0028  * from the binary stream which is generated by the preliminary
0029  * version of LZ4 tool so far.
0030  */
0031 #define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20)
0032 #define ARCHIVE_MAGICNUMBER 0x184C2102
0033 
0034 STATIC inline int INIT unlz4(u8 *input, long in_len,
0035                 long (*fill)(void *, unsigned long),
0036                 long (*flush)(void *, unsigned long),
0037                 u8 *output, long *posp,
0038                 void (*error) (char *x))
0039 {
0040     int ret = -1;
0041     size_t chunksize = 0;
0042     size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE;
0043     u8 *inp;
0044     u8 *inp_start;
0045     u8 *outp;
0046     long size = in_len;
0047 #ifdef PREBOOT
0048     size_t out_len = get_unaligned_le32(input + in_len);
0049 #endif
0050     size_t dest_len;
0051 
0052 
0053     if (output) {
0054         outp = output;
0055     } else if (!flush) {
0056         error("NULL output pointer and no flush function provided");
0057         goto exit_0;
0058     } else {
0059         outp = large_malloc(uncomp_chunksize);
0060         if (!outp) {
0061             error("Could not allocate output buffer");
0062             goto exit_0;
0063         }
0064     }
0065 
0066     if (input && fill) {
0067         error("Both input pointer and fill function provided,");
0068         goto exit_1;
0069     } else if (input) {
0070         inp = input;
0071     } else if (!fill) {
0072         error("NULL input pointer and missing fill function");
0073         goto exit_1;
0074     } else {
0075         inp = large_malloc(lz4_compressbound(uncomp_chunksize));
0076         if (!inp) {
0077             error("Could not allocate input buffer");
0078             goto exit_1;
0079         }
0080     }
0081     inp_start = inp;
0082 
0083     if (posp)
0084         *posp = 0;
0085 
0086     if (fill) {
0087         size = fill(inp, 4);
0088         if (size < 4) {
0089             error("data corrupted");
0090             goto exit_2;
0091         }
0092     }
0093 
0094     chunksize = get_unaligned_le32(inp);
0095     if (chunksize == ARCHIVE_MAGICNUMBER) {
0096         if (!fill) {
0097             inp += 4;
0098             size -= 4;
0099         }
0100     } else {
0101         error("invalid header");
0102         goto exit_2;
0103     }
0104 
0105     if (posp)
0106         *posp += 4;
0107 
0108     for (;;) {
0109 
0110         if (fill) {
0111             size = fill(inp, 4);
0112             if (size == 0)
0113                 break;
0114             if (size < 4) {
0115                 error("data corrupted");
0116                 goto exit_2;
0117             }
0118         }
0119 
0120         chunksize = get_unaligned_le32(inp);
0121         if (chunksize == ARCHIVE_MAGICNUMBER) {
0122             if (!fill) {
0123                 inp += 4;
0124                 size -= 4;
0125             }
0126             if (posp)
0127                 *posp += 4;
0128             continue;
0129         }
0130 
0131 
0132         if (posp)
0133             *posp += 4;
0134 
0135         if (!fill) {
0136             inp += 4;
0137             size -= 4;
0138         } else {
0139             if (chunksize > lz4_compressbound(uncomp_chunksize)) {
0140                 error("chunk length is longer than allocated");
0141                 goto exit_2;
0142             }
0143             size = fill(inp, chunksize);
0144             if (size < chunksize) {
0145                 error("data corrupted");
0146                 goto exit_2;
0147             }
0148         }
0149 #ifdef PREBOOT
0150         if (out_len >= uncomp_chunksize) {
0151             dest_len = uncomp_chunksize;
0152             out_len -= dest_len;
0153         } else
0154             dest_len = out_len;
0155         ret = lz4_decompress(inp, &chunksize, outp, dest_len);
0156 #else
0157         dest_len = uncomp_chunksize;
0158         ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp,
0159                 &dest_len);
0160 #endif
0161         if (ret < 0) {
0162             error("Decoding failed");
0163             goto exit_2;
0164         }
0165 
0166         ret = -1;
0167         if (flush && flush(outp, dest_len) != dest_len)
0168             goto exit_2;
0169         if (output)
0170             outp += dest_len;
0171         if (posp)
0172             *posp += chunksize;
0173 
0174         if (!fill) {
0175             size -= chunksize;
0176 
0177             if (size == 0)
0178                 break;
0179             else if (size < 0) {
0180                 error("data corrupted");
0181                 goto exit_2;
0182             }
0183             inp += chunksize;
0184         }
0185     }
0186 
0187     ret = 0;
0188 exit_2:
0189     if (!input)
0190         large_free(inp_start);
0191 exit_1:
0192     if (!output)
0193         large_free(outp);
0194 exit_0:
0195     return ret;
0196 }
0197 
0198 #ifdef PREBOOT
0199 STATIC int INIT __decompress(unsigned char *buf, long in_len,
0200                   long (*fill)(void*, unsigned long),
0201                   long (*flush)(void*, unsigned long),
0202                   unsigned char *output, long out_len,
0203                   long *posp,
0204                   void (*error)(char *x)
0205     )
0206 {
0207     return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
0208 }
0209 #endif