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 } else if (size < 4) { 116 /* empty or end-of-file */ 117 goto exit_3; 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 if (!fill && chunksize == 0) { 132 /* empty or end-of-file */ 133 goto exit_3; 134 } 135 136 if (posp) 137 *posp += 4; 138 139 if (!fill) { 140 inp += 4; 141 size -= 4; 142 } else { 143 if (chunksize > LZ4_compressBound(uncomp_chunksize)) { 144 error("chunk length is longer than allocated"); 145 goto exit_2; 146 } 147 size = fill(inp, chunksize); 148 if (size < chunksize) { 149 error("data corrupted"); 150 goto exit_2; 151 } 152 } 153 #ifdef PREBOOT 154 if (out_len >= uncomp_chunksize) { 155 dest_len = uncomp_chunksize; 156 out_len -= dest_len; 157 } else 158 dest_len = out_len; 159 160 ret = LZ4_decompress_fast(inp, outp, dest_len); 161 chunksize = ret; 162 #else 163 dest_len = uncomp_chunksize; 164 165 ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len); 166 dest_len = ret; 167 #endif 168 if (ret < 0) { 169 error("Decoding failed"); 170 goto exit_2; 171 } 172 173 ret = -1; 174 if (flush && flush(outp, dest_len) != dest_len) 175 goto exit_2; 176 if (output) 177 outp += dest_len; 178 if (posp) 179 *posp += chunksize; 180 181 if (!fill) { 182 size -= chunksize; 183 184 if (size == 0) 185 break; 186 else if (size < 0) { 187 error("data corrupted"); 188 goto exit_2; 189 } 190 inp += chunksize; 191 } 192 } 193 194 exit_3: 195 ret = 0; 196 exit_2: 197 if (!input) 198 large_free(inp_start); 199 exit_1: 200 if (!output) 201 large_free(outp); 202 exit_0: 203 return ret; 204 } 205 206 #ifdef PREBOOT 207 STATIC int INIT __decompress(unsigned char *buf, long in_len, 208 long (*fill)(void*, unsigned long), 209 long (*flush)(void*, unsigned long), 210 unsigned char *output, long out_len, 211 long *posp, 212 void (*error)(char *x) 213 ) 214 { 215 return unlz4(buf, in_len - 4, fill, flush, output, posp, error); 216 } 217 #endif 218
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.