Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only 2 : : /* 3 : : * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd 4 : : * 5 : : * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com> 6 : : */ 7 : : 8 : : #ifdef STATIC 9 : : #define PREBOOT 10 : : #include "lz4/lz4_decompress.c" 11 : : #else 12 : : #include <linux/decompress/unlz4.h> 13 : : #endif 14 : : #include <linux/types.h> 15 : : #include <linux/lz4.h> 16 : : #include <linux/decompress/mm.h> 17 : : #include <linux/compiler.h> 18 : : 19 : : #include <asm/unaligned.h> 20 : : 21 : : /* 22 : : * Note: Uncompressed chunk size is used in the compressor side 23 : : * (userspace side for compression). 24 : : * It is hardcoded because there is not proper way to extract it 25 : : * from the binary stream which is generated by the preliminary 26 : : * version of LZ4 tool so far. 27 : : */ 28 : : #define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20) 29 : : #define ARCHIVE_MAGICNUMBER 0x184C2102 30 : : 31 : 0 : STATIC inline int INIT unlz4(u8 *input, long in_len, 32 : : long (*fill)(void *, unsigned long), 33 : : long (*flush)(void *, unsigned long), 34 : : u8 *output, long *posp, 35 : : void (*error) (char *x)) 36 : : { 37 : : int ret = -1; 38 : : size_t chunksize = 0; 39 : : size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE; 40 : : u8 *inp; 41 : : u8 *inp_start; 42 : : u8 *outp; 43 : : long size = in_len; 44 : : #ifdef PREBOOT 45 : : size_t out_len = get_unaligned_le32(input + in_len); 46 : : #endif 47 : : size_t dest_len; 48 : : 49 : : 50 : 0 : if (output) { 51 : : outp = output; 52 : 0 : } else if (!flush) { 53 : 0 : error("NULL output pointer and no flush function provided"); 54 : 0 : goto exit_0; 55 : : } else { 56 : 0 : outp = large_malloc(uncomp_chunksize); 57 : 0 : if (!outp) { 58 : 0 : error("Could not allocate output buffer"); 59 : 0 : goto exit_0; 60 : : } 61 : : } 62 : : 63 : 0 : if (input && fill) { 64 : 0 : error("Both input pointer and fill function provided,"); 65 : 0 : goto exit_1; 66 : 0 : } else if (input) { 67 : : inp = input; 68 : 0 : } else if (!fill) { 69 : 0 : error("NULL input pointer and missing fill function"); 70 : 0 : goto exit_1; 71 : : } else { 72 : 0 : inp = large_malloc(LZ4_compressBound(uncomp_chunksize)); 73 : 0 : if (!inp) { 74 : 0 : error("Could not allocate input buffer"); 75 : 0 : goto exit_1; 76 : : } 77 : : } 78 : : inp_start = inp; 79 : : 80 : 0 : if (posp) 81 : 0 : *posp = 0; 82 : : 83 : 0 : if (fill) { 84 : 0 : size = fill(inp, 4); 85 : 0 : if (size < 4) { 86 : 0 : error("data corrupted"); 87 : 0 : goto exit_2; 88 : : } 89 : : } 90 : : 91 : : chunksize = get_unaligned_le32(inp); 92 : 0 : if (chunksize == ARCHIVE_MAGICNUMBER) { 93 : 0 : if (!fill) { 94 : 0 : inp += 4; 95 : 0 : size -= 4; 96 : : } 97 : : } else { 98 : 0 : error("invalid header"); 99 : 0 : goto exit_2; 100 : : } 101 : : 102 : 0 : if (posp) 103 : 0 : *posp += 4; 104 : : 105 : : for (;;) { 106 : : 107 : 0 : if (fill) { 108 : 0 : size = fill(inp, 4); 109 : 0 : if (size == 0) 110 : : break; 111 : 0 : if (size < 4) { 112 : 0 : error("data corrupted"); 113 : 0 : goto exit_2; 114 : : } 115 : : } 116 : : 117 : : chunksize = get_unaligned_le32(inp); 118 : 0 : if (chunksize == ARCHIVE_MAGICNUMBER) { 119 : 0 : if (!fill) { 120 : 0 : inp += 4; 121 : 0 : size -= 4; 122 : : } 123 : 0 : if (posp) 124 : 0 : *posp += 4; 125 : 0 : continue; 126 : : } 127 : : 128 : : 129 : 0 : if (posp) 130 : 0 : *posp += 4; 131 : : 132 : 0 : if (!fill) { 133 : 0 : inp += 4; 134 : 0 : size -= 4; 135 : : } else { 136 : 0 : if (chunksize > LZ4_compressBound(uncomp_chunksize)) { 137 : 0 : error("chunk length is longer than allocated"); 138 : 0 : goto exit_2; 139 : : } 140 : 0 : size = fill(inp, chunksize); 141 : 0 : if (size < chunksize) { 142 : 0 : error("data corrupted"); 143 : 0 : goto exit_2; 144 : : } 145 : : } 146 : : #ifdef PREBOOT 147 : : if (out_len >= uncomp_chunksize) { 148 : : dest_len = uncomp_chunksize; 149 : : out_len -= dest_len; 150 : : } else 151 : : dest_len = out_len; 152 : : 153 : : ret = LZ4_decompress_fast(inp, outp, dest_len); 154 : : chunksize = ret; 155 : : #else 156 : : dest_len = uncomp_chunksize; 157 : : 158 : 0 : ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len); 159 : 0 : dest_len = ret; 160 : : #endif 161 : 0 : if (ret < 0) { 162 : 0 : error("Decoding failed"); 163 : 0 : goto exit_2; 164 : : } 165 : : 166 : : ret = -1; 167 : 0 : if (flush && flush(outp, dest_len) != dest_len) 168 : : goto exit_2; 169 : 0 : if (output) 170 : 0 : outp += dest_len; 171 : 0 : if (posp) 172 : 0 : *posp += chunksize; 173 : : 174 : 0 : if (!fill) { 175 : 0 : size -= chunksize; 176 : : 177 : 0 : if (size == 0) 178 : : break; 179 : 0 : else if (size < 0) { 180 : 0 : error("data corrupted"); 181 : 0 : goto exit_2; 182 : : } 183 : 0 : inp += chunksize; 184 : : } 185 : : } 186 : : 187 : : ret = 0; 188 : : exit_2: 189 : 0 : if (!input) 190 : 0 : large_free(inp_start); 191 : : exit_1: 192 : 0 : if (!output) 193 : 0 : large_free(outp); 194 : : exit_0: 195 : 0 : return ret; 196 : : } 197 : : 198 : : #ifdef PREBOOT 199 : : STATIC 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