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 ret = lz4_decompress(inp, &chunksize, outp, dest_len); 156#else 157 dest_len = uncomp_chunksize; 158 ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp, 159 &dest_len); 160#endif 161 if (ret < 0) { 162 error("Decoding failed"); 163 goto exit_2; 164 } 165 166 ret = -1; 167 if (flush && flush(outp, dest_len) != dest_len) 168 goto exit_2; 169 if (output) 170 outp += dest_len; 171 if (posp) 172 *posp += chunksize; 173 174 if (!fill) { 175 size -= chunksize; 176 177 if (size == 0) 178 break; 179 else if (size < 0) { 180 error("data corrupted"); 181 goto exit_2; 182 } 183 inp += chunksize; 184 } 185 } 186 187 ret = 0; 188exit_2: 189 if (!input) 190 large_free(inp_start); 191exit_1: 192 if (!output) 193 large_free(outp); 194exit_0: 195 return ret; 196} 197 198#ifdef PREBOOT 199STATIC int INIT __decompress(unsigned char *buf, long in_len, 200 long (*fill)(void*, unsigned long), 201 long (*flush)(void*, unsigned long), 202 unsigned char *output, long out_len, 203 long *posp, 204 void (*error)(char *x) 205 ) 206{ 207 return unlz4(buf, in_len - 4, fill, flush, output, posp, error); 208} 209#endif 210