1*d2912cb1SThomas 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 } 1154d4b866aSYinghai Lu } 116e76e1fdfSKyungsik Lee 117e76e1fdfSKyungsik Lee chunksize = get_unaligned_le32(inp); 118e76e1fdfSKyungsik Lee if (chunksize == ARCHIVE_MAGICNUMBER) { 1194d4b866aSYinghai Lu if (!fill) { 120e76e1fdfSKyungsik Lee inp += 4; 121e76e1fdfSKyungsik Lee size -= 4; 1224d4b866aSYinghai Lu } 123e76e1fdfSKyungsik Lee if (posp) 124e76e1fdfSKyungsik Lee *posp += 4; 125e76e1fdfSKyungsik Lee continue; 126e76e1fdfSKyungsik Lee } 1274d4b866aSYinghai Lu 128e76e1fdfSKyungsik Lee 129e76e1fdfSKyungsik Lee if (posp) 130e76e1fdfSKyungsik Lee *posp += 4; 131e76e1fdfSKyungsik Lee 1324d4b866aSYinghai Lu if (!fill) { 1334d4b866aSYinghai Lu inp += 4; 1344d4b866aSYinghai Lu size -= 4; 1354d4b866aSYinghai Lu } else { 136e23d54e4SSven Schmidt if (chunksize > LZ4_compressBound(uncomp_chunksize)) { 137e76e1fdfSKyungsik Lee error("chunk length is longer than allocated"); 138e76e1fdfSKyungsik Lee goto exit_2; 139e76e1fdfSKyungsik Lee } 1404d4b866aSYinghai Lu size = fill(inp, chunksize); 1414d4b866aSYinghai Lu if (size < chunksize) { 1424d4b866aSYinghai Lu error("data corrupted"); 1434d4b866aSYinghai Lu goto exit_2; 1444d4b866aSYinghai Lu } 145e76e1fdfSKyungsik Lee } 146e76e1fdfSKyungsik Lee #ifdef PREBOOT 147e76e1fdfSKyungsik Lee if (out_len >= uncomp_chunksize) { 148e76e1fdfSKyungsik Lee dest_len = uncomp_chunksize; 149e76e1fdfSKyungsik Lee out_len -= dest_len; 150e76e1fdfSKyungsik Lee } else 151e76e1fdfSKyungsik Lee dest_len = out_len; 152e23d54e4SSven Schmidt 153e23d54e4SSven Schmidt ret = LZ4_decompress_fast(inp, outp, dest_len); 154e23d54e4SSven Schmidt chunksize = ret; 155e76e1fdfSKyungsik Lee #else 156e76e1fdfSKyungsik Lee dest_len = uncomp_chunksize; 157e23d54e4SSven Schmidt 158e23d54e4SSven Schmidt ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len); 159e23d54e4SSven Schmidt dest_len = ret; 160e76e1fdfSKyungsik Lee #endif 161e76e1fdfSKyungsik Lee if (ret < 0) { 162e76e1fdfSKyungsik Lee error("Decoding failed"); 163e76e1fdfSKyungsik Lee goto exit_2; 164e76e1fdfSKyungsik Lee } 165e76e1fdfSKyungsik Lee 1662a1d689cSJan Beulich ret = -1; 167e76e1fdfSKyungsik Lee if (flush && flush(outp, dest_len) != dest_len) 168e76e1fdfSKyungsik Lee goto exit_2; 169e76e1fdfSKyungsik Lee if (output) 170e76e1fdfSKyungsik Lee outp += dest_len; 171e76e1fdfSKyungsik Lee if (posp) 172e76e1fdfSKyungsik Lee *posp += chunksize; 173e76e1fdfSKyungsik Lee 1744d4b866aSYinghai Lu if (!fill) { 175e76e1fdfSKyungsik Lee size -= chunksize; 176e76e1fdfSKyungsik Lee 177e76e1fdfSKyungsik Lee if (size == 0) 178e76e1fdfSKyungsik Lee break; 179e76e1fdfSKyungsik Lee else if (size < 0) { 180e76e1fdfSKyungsik Lee error("data corrupted"); 181e76e1fdfSKyungsik Lee goto exit_2; 182e76e1fdfSKyungsik Lee } 183e76e1fdfSKyungsik Lee inp += chunksize; 1844d4b866aSYinghai Lu } 185e76e1fdfSKyungsik Lee } 186e76e1fdfSKyungsik Lee 187e76e1fdfSKyungsik Lee ret = 0; 188e76e1fdfSKyungsik Lee exit_2: 189e76e1fdfSKyungsik Lee if (!input) 190e76e1fdfSKyungsik Lee large_free(inp_start); 191e76e1fdfSKyungsik Lee exit_1: 192e76e1fdfSKyungsik Lee if (!output) 193e76e1fdfSKyungsik Lee large_free(outp); 194e76e1fdfSKyungsik Lee exit_0: 195e76e1fdfSKyungsik Lee return ret; 196e76e1fdfSKyungsik Lee } 197e76e1fdfSKyungsik Lee 198e76e1fdfSKyungsik Lee #ifdef PREBOOT 1992d3862d2SYinghai Lu STATIC int INIT __decompress(unsigned char *buf, long in_len, 200d97b07c5SYinghai Lu long (*fill)(void*, unsigned long), 201d97b07c5SYinghai Lu long (*flush)(void*, unsigned long), 2022d3862d2SYinghai Lu unsigned char *output, long out_len, 203d97b07c5SYinghai Lu long *posp, 204e76e1fdfSKyungsik Lee void (*error)(char *x) 205e76e1fdfSKyungsik Lee ) 206e76e1fdfSKyungsik Lee { 207e76e1fdfSKyungsik Lee return unlz4(buf, in_len - 4, fill, flush, output, posp, error); 208e76e1fdfSKyungsik Lee } 209e76e1fdfSKyungsik Lee #endif 210