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 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 if (output) { 51 outp = output; 52 } else if (!flush) { 53 error("NULL output pointer and no flush function provided"); 54 goto exit_0; 55 } else { 56 outp = large_malloc(uncomp_chunksize); 57 if (!outp) { 58 error("Could not allocate output buffer"); 59 goto exit_0; 60 } 61 } 62 63 if (input && fill) { 64 error("Both input pointer and fill function provided,"); 65 goto exit_1; 66 } else if (input) { 67 inp = input; 68 } else if (!fill) { 69 error("NULL input pointer and missing fill function"); 70 goto exit_1; 71 } else { 72 inp = large_malloc(LZ4_compressBound(uncomp_chunksize)); 73 if (!inp) { 74 error("Could not allocate input buffer"); 75 goto exit_1; 76 } 77 } 78 inp_start = inp; 79 80 if (posp) 81 *posp = 0; 82 83 if (fill) { 84 size = fill(inp, 4); 85 if (size < 4) { 86 error("data corrupted"); 87 goto exit_2; 88 } 89 } 90 91 chunksize = get_unaligned_le32(inp); 92 if (chunksize == ARCHIVE_MAGICNUMBER) { 93 if (!fill) { 94 inp += 4; 95 size -= 4; 96 } 97 } else { 98 error("invalid header"); 99 goto exit_2; 100 } 101 102 if (posp) 103 *posp += 4; 104 105 for (;;) { 106 107 if (fill) { 108 size = fill(inp, 4); 109 if (size == 0) 110 break; 111 if (size < 4) { 112 error("data corrupted"); 113 goto exit_2; 114 } 115 } 116 117 chunksize = get_unaligned_le32(inp); 118 if (chunksize == ARCHIVE_MAGICNUMBER) { 119 if (!fill) { 120 inp += 4; 121 size -= 4; 122 } 123 if (posp) 124 *posp += 4; 125 continue; 126 } 127 128 129 if (posp) 130 *posp += 4; 131 132 if (!fill) { 133 inp += 4; 134 size -= 4; 135 } else { 136 if (chunksize > LZ4_compressBound(uncomp_chunksize)) { 137 error("chunk length is longer than allocated"); 138 goto exit_2; 139 } 140 size = fill(inp, chunksize); 141 if (size < chunksize) { 142 error("data corrupted"); 143 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 ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len); 159 dest_len = ret; 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; 188 exit_2: 189 if (!input) 190 large_free(inp_start); 191 exit_1: 192 if (!output) 193 large_free(outp); 194 exit_0: 195 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 210