113546Sjulian// SPDX-License-Identifier: GPL-2.0-only 235509Sjb/* 313546Sjulian * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd 413546Sjulian * 513546Sjulian * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com> 613546Sjulian */ 713546Sjulian 813546Sjulian#ifdef STATIC 913546Sjulian#define PREBOOT 1013546Sjulian#include "lz4/lz4_decompress.c" 1113546Sjulian#else 1213546Sjulian#include <linux/decompress/unlz4.h> 13165967Simp#endif 1413546Sjulian#include <linux/types.h> 1513546Sjulian#include <linux/lz4.h> 1613546Sjulian#include <linux/decompress/mm.h> 1713546Sjulian#include <linux/compiler.h> 1813546Sjulian 1913546Sjulian#include <asm/unaligned.h> 2049439Sdeischen 2113546Sjulian/* 2213546Sjulian * Note: Uncompressed chunk size is used in the compressor side 2313546Sjulian * (userspace side for compression). 2413546Sjulian * It is hardcoded because there is not proper way to extract it 2513546Sjulian * from the binary stream which is generated by the preliminary 2613546Sjulian * version of LZ4 tool so far. 2713546Sjulian */ 2813546Sjulian#define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20) 2950476Speter#define ARCHIVE_MAGICNUMBER 0x184C2102 3025402Sjb 3113546SjulianSTATIC inline int INIT unlz4(u8 *input, long in_len, 32174112Sdeischen long (*fill)(void *, unsigned long), 33174112Sdeischen long (*flush)(void *, unsigned long), 3413546Sjulian u8 *output, long *posp, 3517706Sjulian void (*error) (char *x)) 3613546Sjulian{ 3713546Sjulian int ret = -1; 3825402Sjb size_t chunksize = 0; 3913546Sjulian size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE; 40174112Sdeischen u8 *inp; 41103388Smini u8 *inp_start; 4213546Sjulian u8 *outp; 43174112Sdeischen long size = in_len; 44174112Sdeischen#ifdef PREBOOT 4575658Sdeischen size_t out_len = get_unaligned_le32(input + in_len); 4671581Sdeischen#endif 4713546Sjulian size_t dest_len; 4871581Sdeischen 4956698Sjasone 50113658Sdeischen if (output) { 5156698Sjasone outp = output; 5256698Sjasone } else if (!flush) { 5356698Sjasone error("NULL output pointer and no flush function provided"); 5456698Sjasone goto exit_0; 55123312Sdavidxu } else { 5656698Sjasone outp = large_malloc(uncomp_chunksize); 5756698Sjasone if (!outp) { 5856698Sjasone error("Could not allocate output buffer"); 5956698Sjasone goto exit_0; 6056698Sjasone } 6156698Sjasone } 6256698Sjasone 6356698Sjasone if (input && fill) { 6456698Sjasone error("Both input pointer and fill function provided,"); 65103419Smini goto exit_1; 66123312Sdavidxu } else if (input) { 67123312Sdavidxu inp = input; 68123312Sdavidxu } else if (!fill) { 69123312Sdavidxu error("NULL input pointer and missing fill function"); 70123312Sdavidxu goto exit_1; 7156698Sjasone } else { 7256698Sjasone inp = large_malloc(LZ4_compressBound(uncomp_chunksize)); 7356698Sjasone 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 194exit_3: 195 ret = 0; 196exit_2: 197 if (!input) 198 large_free(inp_start); 199exit_1: 200 if (!output) 201 large_free(outp); 202exit_0: 203 return ret; 204} 205 206#ifdef PREBOOT 207STATIC 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