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 34 STATIC 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 ret = -1; 145 if (flush && flush(outp, dest_len) != dest_len) 146 goto exit_2; 147 if (output) 148 outp += dest_len; 149 if (posp) 150 *posp += chunksize; 151 152 size -= chunksize; 153 154 if (size == 0) 155 break; 156 else if (size < 0) { 157 error("data corrupted"); 158 goto exit_2; 159 } 160 161 inp += chunksize; 162 if (fill) 163 inp = inp_start; 164 } 165 166 ret = 0; 167 exit_2: 168 if (!input) 169 large_free(inp_start); 170 exit_1: 171 if (!output) 172 large_free(outp); 173 exit_0: 174 return ret; 175 } 176 177 #ifdef PREBOOT 178 STATIC int INIT decompress(unsigned char *buf, int in_len, 179 int(*fill)(void*, unsigned int), 180 int(*flush)(void*, unsigned int), 181 unsigned char *output, 182 int *posp, 183 void(*error)(char *x) 184 ) 185 { 186 return unlz4(buf, in_len - 4, fill, flush, output, posp, error); 187 } 188 #endif 189