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, 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; 191 exit_2: 192 if (!input) 193 large_free(inp_start); 194 exit_1: 195 if (!output) 196 large_free(outp); 197 exit_0: 198 return ret; 199 } 200 201 #ifdef PREBOOT 202 STATIC 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