1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2e76e1fdfSKyungsik Lee /* 3e76e1fdfSKyungsik Lee * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd 4e76e1fdfSKyungsik Lee * 5e76e1fdfSKyungsik Lee * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com> 6e76e1fdfSKyungsik Lee */ 7e76e1fdfSKyungsik Lee 8e76e1fdfSKyungsik Lee #ifdef STATIC 9e76e1fdfSKyungsik Lee #define PREBOOT 10e76e1fdfSKyungsik Lee #include "lz4/lz4_decompress.c" 11e76e1fdfSKyungsik Lee #else 12e76e1fdfSKyungsik Lee #include <linux/decompress/unlz4.h> 13e76e1fdfSKyungsik Lee #endif 14e76e1fdfSKyungsik Lee #include <linux/types.h> 15e76e1fdfSKyungsik Lee #include <linux/lz4.h> 16e76e1fdfSKyungsik Lee #include <linux/decompress/mm.h> 17e76e1fdfSKyungsik Lee #include <linux/compiler.h> 18e76e1fdfSKyungsik Lee 19e76e1fdfSKyungsik Lee #include <asm/unaligned.h> 20e76e1fdfSKyungsik Lee 21e76e1fdfSKyungsik Lee /* 22e76e1fdfSKyungsik Lee * Note: Uncompressed chunk size is used in the compressor side 23e76e1fdfSKyungsik Lee * (userspace side for compression). 24e76e1fdfSKyungsik Lee * It is hardcoded because there is not proper way to extract it 25e76e1fdfSKyungsik Lee * from the binary stream which is generated by the preliminary 26e76e1fdfSKyungsik Lee * version of LZ4 tool so far. 27e76e1fdfSKyungsik Lee */ 28e76e1fdfSKyungsik Lee #define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20) 29e76e1fdfSKyungsik Lee #define ARCHIVE_MAGICNUMBER 0x184C2102 30e76e1fdfSKyungsik Lee 31d97b07c5SYinghai Lu STATIC inline int INIT unlz4(u8 *input, long in_len, 32d97b07c5SYinghai Lu long (*fill)(void *, unsigned long), 33d97b07c5SYinghai Lu long (*flush)(void *, unsigned long), 34d97b07c5SYinghai Lu u8 *output, long *posp, 35e76e1fdfSKyungsik Lee void (*error) (char *x)) 36e76e1fdfSKyungsik Lee { 37e76e1fdfSKyungsik Lee int ret = -1; 38e76e1fdfSKyungsik Lee size_t chunksize = 0; 39e76e1fdfSKyungsik Lee size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE; 40e76e1fdfSKyungsik Lee u8 *inp; 41e76e1fdfSKyungsik Lee u8 *inp_start; 42e76e1fdfSKyungsik Lee u8 *outp; 43d97b07c5SYinghai Lu long size = in_len; 44e76e1fdfSKyungsik Lee #ifdef PREBOOT 45e76e1fdfSKyungsik Lee size_t out_len = get_unaligned_le32(input + in_len); 46e76e1fdfSKyungsik Lee #endif 47e76e1fdfSKyungsik Lee size_t dest_len; 48e76e1fdfSKyungsik Lee 49e76e1fdfSKyungsik Lee 50e76e1fdfSKyungsik Lee if (output) { 51e76e1fdfSKyungsik Lee outp = output; 52e76e1fdfSKyungsik Lee } else if (!flush) { 53e76e1fdfSKyungsik Lee error("NULL output pointer and no flush function provided"); 54e76e1fdfSKyungsik Lee goto exit_0; 55e76e1fdfSKyungsik Lee } else { 56e76e1fdfSKyungsik Lee outp = large_malloc(uncomp_chunksize); 57e76e1fdfSKyungsik Lee if (!outp) { 58e76e1fdfSKyungsik Lee error("Could not allocate output buffer"); 59e76e1fdfSKyungsik Lee goto exit_0; 60e76e1fdfSKyungsik Lee } 61e76e1fdfSKyungsik Lee } 62e76e1fdfSKyungsik Lee 63e76e1fdfSKyungsik Lee if (input && fill) { 64e76e1fdfSKyungsik Lee error("Both input pointer and fill function provided,"); 65e76e1fdfSKyungsik Lee goto exit_1; 66e76e1fdfSKyungsik Lee } else if (input) { 67e76e1fdfSKyungsik Lee inp = input; 68e76e1fdfSKyungsik Lee } else if (!fill) { 69e76e1fdfSKyungsik Lee error("NULL input pointer and missing fill function"); 70e76e1fdfSKyungsik Lee goto exit_1; 71e76e1fdfSKyungsik Lee } else { 72e23d54e4SSven Schmidt inp = large_malloc(LZ4_compressBound(uncomp_chunksize)); 73e76e1fdfSKyungsik Lee if (!inp) { 74e76e1fdfSKyungsik Lee error("Could not allocate input buffer"); 75e76e1fdfSKyungsik Lee goto exit_1; 76e76e1fdfSKyungsik Lee } 77e76e1fdfSKyungsik Lee } 78e76e1fdfSKyungsik Lee inp_start = inp; 79e76e1fdfSKyungsik Lee 80e76e1fdfSKyungsik Lee if (posp) 81e76e1fdfSKyungsik Lee *posp = 0; 82e76e1fdfSKyungsik Lee 834d4b866aSYinghai Lu if (fill) { 844d4b866aSYinghai Lu size = fill(inp, 4); 854d4b866aSYinghai Lu if (size < 4) { 864d4b866aSYinghai Lu error("data corrupted"); 874d4b866aSYinghai Lu goto exit_2; 884d4b866aSYinghai Lu } 894d4b866aSYinghai Lu } 90e76e1fdfSKyungsik Lee 91e76e1fdfSKyungsik Lee chunksize = get_unaligned_le32(inp); 92e76e1fdfSKyungsik Lee if (chunksize == ARCHIVE_MAGICNUMBER) { 934d4b866aSYinghai Lu if (!fill) { 94e76e1fdfSKyungsik Lee inp += 4; 95e76e1fdfSKyungsik Lee size -= 4; 964d4b866aSYinghai Lu } 97e76e1fdfSKyungsik Lee } else { 98e76e1fdfSKyungsik Lee error("invalid header"); 99e76e1fdfSKyungsik Lee goto exit_2; 100e76e1fdfSKyungsik Lee } 101e76e1fdfSKyungsik Lee 102e76e1fdfSKyungsik Lee if (posp) 103e76e1fdfSKyungsik Lee *posp += 4; 104e76e1fdfSKyungsik Lee 105e76e1fdfSKyungsik Lee for (;;) { 106e76e1fdfSKyungsik Lee 1074d4b866aSYinghai Lu if (fill) { 1084d4b866aSYinghai Lu size = fill(inp, 4); 1094d4b866aSYinghai Lu if (size == 0) 1104d4b866aSYinghai Lu break; 1114d4b866aSYinghai Lu if (size < 4) { 1124d4b866aSYinghai Lu error("data corrupted"); 1134d4b866aSYinghai Lu goto exit_2; 1144d4b866aSYinghai Lu } 115*2c484419SDimitri John Ledkov } else if (size < 4) { 116*2c484419SDimitri John Ledkov /* empty or end-of-file */ 117*2c484419SDimitri John Ledkov goto exit_3; 1184d4b866aSYinghai Lu } 119e76e1fdfSKyungsik Lee 120e76e1fdfSKyungsik Lee chunksize = get_unaligned_le32(inp); 121e76e1fdfSKyungsik Lee if (chunksize == ARCHIVE_MAGICNUMBER) { 1224d4b866aSYinghai Lu if (!fill) { 123e76e1fdfSKyungsik Lee inp += 4; 124e76e1fdfSKyungsik Lee size -= 4; 1254d4b866aSYinghai Lu } 126e76e1fdfSKyungsik Lee if (posp) 127e76e1fdfSKyungsik Lee *posp += 4; 128e76e1fdfSKyungsik Lee continue; 129e76e1fdfSKyungsik Lee } 1304d4b866aSYinghai Lu 131*2c484419SDimitri John Ledkov if (!fill && chunksize == 0) { 132*2c484419SDimitri John Ledkov /* empty or end-of-file */ 133*2c484419SDimitri John Ledkov goto exit_3; 134*2c484419SDimitri John Ledkov } 135e76e1fdfSKyungsik Lee 136e76e1fdfSKyungsik Lee if (posp) 137e76e1fdfSKyungsik Lee *posp += 4; 138e76e1fdfSKyungsik Lee 1394d4b866aSYinghai Lu if (!fill) { 1404d4b866aSYinghai Lu inp += 4; 1414d4b866aSYinghai Lu size -= 4; 1424d4b866aSYinghai Lu } else { 143e23d54e4SSven Schmidt if (chunksize > LZ4_compressBound(uncomp_chunksize)) { 144e76e1fdfSKyungsik Lee error("chunk length is longer than allocated"); 145e76e1fdfSKyungsik Lee goto exit_2; 146e76e1fdfSKyungsik Lee } 1474d4b866aSYinghai Lu size = fill(inp, chunksize); 1484d4b866aSYinghai Lu if (size < chunksize) { 1494d4b866aSYinghai Lu error("data corrupted"); 1504d4b866aSYinghai Lu goto exit_2; 1514d4b866aSYinghai Lu } 152e76e1fdfSKyungsik Lee } 153e76e1fdfSKyungsik Lee #ifdef PREBOOT 154e76e1fdfSKyungsik Lee if (out_len >= uncomp_chunksize) { 155e76e1fdfSKyungsik Lee dest_len = uncomp_chunksize; 156e76e1fdfSKyungsik Lee out_len -= dest_len; 157e76e1fdfSKyungsik Lee } else 158e76e1fdfSKyungsik Lee dest_len = out_len; 159e23d54e4SSven Schmidt 160e23d54e4SSven Schmidt ret = LZ4_decompress_fast(inp, outp, dest_len); 161e23d54e4SSven Schmidt chunksize = ret; 162e76e1fdfSKyungsik Lee #else 163e76e1fdfSKyungsik Lee dest_len = uncomp_chunksize; 164e23d54e4SSven Schmidt 165e23d54e4SSven Schmidt ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len); 166e23d54e4SSven Schmidt dest_len = ret; 167e76e1fdfSKyungsik Lee #endif 168e76e1fdfSKyungsik Lee if (ret < 0) { 169e76e1fdfSKyungsik Lee error("Decoding failed"); 170e76e1fdfSKyungsik Lee goto exit_2; 171e76e1fdfSKyungsik Lee } 172e76e1fdfSKyungsik Lee 1732a1d689cSJan Beulich ret = -1; 174e76e1fdfSKyungsik Lee if (flush && flush(outp, dest_len) != dest_len) 175e76e1fdfSKyungsik Lee goto exit_2; 176e76e1fdfSKyungsik Lee if (output) 177e76e1fdfSKyungsik Lee outp += dest_len; 178e76e1fdfSKyungsik Lee if (posp) 179e76e1fdfSKyungsik Lee *posp += chunksize; 180e76e1fdfSKyungsik Lee 1814d4b866aSYinghai Lu if (!fill) { 182e76e1fdfSKyungsik Lee size -= chunksize; 183e76e1fdfSKyungsik Lee 184e76e1fdfSKyungsik Lee if (size == 0) 185e76e1fdfSKyungsik Lee break; 186e76e1fdfSKyungsik Lee else if (size < 0) { 187e76e1fdfSKyungsik Lee error("data corrupted"); 188e76e1fdfSKyungsik Lee goto exit_2; 189e76e1fdfSKyungsik Lee } 190e76e1fdfSKyungsik Lee inp += chunksize; 1914d4b866aSYinghai Lu } 192e76e1fdfSKyungsik Lee } 193e76e1fdfSKyungsik Lee 194*2c484419SDimitri John Ledkov exit_3: 195e76e1fdfSKyungsik Lee ret = 0; 196e76e1fdfSKyungsik Lee exit_2: 197e76e1fdfSKyungsik Lee if (!input) 198e76e1fdfSKyungsik Lee large_free(inp_start); 199e76e1fdfSKyungsik Lee exit_1: 200e76e1fdfSKyungsik Lee if (!output) 201e76e1fdfSKyungsik Lee large_free(outp); 202e76e1fdfSKyungsik Lee exit_0: 203e76e1fdfSKyungsik Lee return ret; 204e76e1fdfSKyungsik Lee } 205e76e1fdfSKyungsik Lee 206e76e1fdfSKyungsik Lee #ifdef PREBOOT 2072d3862d2SYinghai Lu STATIC int INIT __decompress(unsigned char *buf, long in_len, 208d97b07c5SYinghai Lu long (*fill)(void*, unsigned long), 209d97b07c5SYinghai Lu long (*flush)(void*, unsigned long), 2102d3862d2SYinghai Lu unsigned char *output, long out_len, 211d97b07c5SYinghai Lu long *posp, 212e76e1fdfSKyungsik Lee void (*error)(char *x) 213e76e1fdfSKyungsik Lee ) 214e76e1fdfSKyungsik Lee { 215e76e1fdfSKyungsik Lee return unlz4(buf, in_len - 4, fill, flush, output, posp, error); 216e76e1fdfSKyungsik Lee } 217e76e1fdfSKyungsik Lee #endif 218