1e76e1fdfSKyungsik Lee /* 2e76e1fdfSKyungsik Lee * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd 3e76e1fdfSKyungsik Lee * 4e76e1fdfSKyungsik Lee * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com> 5e76e1fdfSKyungsik Lee * 6e76e1fdfSKyungsik Lee * This program is free software; you can redistribute it and/or modify 7e76e1fdfSKyungsik Lee * it under the terms of the GNU General Public License version 2 as 8e76e1fdfSKyungsik Lee * published by the Free Software Foundation. 9e76e1fdfSKyungsik Lee */ 10e76e1fdfSKyungsik Lee 11e76e1fdfSKyungsik Lee #ifdef STATIC 12e76e1fdfSKyungsik Lee #define PREBOOT 13e76e1fdfSKyungsik Lee #include "lz4/lz4_decompress.c" 14e76e1fdfSKyungsik Lee #else 15e76e1fdfSKyungsik Lee #include <linux/decompress/unlz4.h> 16e76e1fdfSKyungsik Lee #endif 17e76e1fdfSKyungsik Lee #include <linux/types.h> 18e76e1fdfSKyungsik Lee #include <linux/lz4.h> 19e76e1fdfSKyungsik Lee #include <linux/decompress/mm.h> 20e76e1fdfSKyungsik Lee #include <linux/compiler.h> 21e76e1fdfSKyungsik Lee 22e76e1fdfSKyungsik Lee #include <asm/unaligned.h> 23e76e1fdfSKyungsik Lee 24e76e1fdfSKyungsik Lee /* 25e76e1fdfSKyungsik Lee * Note: Uncompressed chunk size is used in the compressor side 26e76e1fdfSKyungsik Lee * (userspace side for compression). 27e76e1fdfSKyungsik Lee * It is hardcoded because there is not proper way to extract it 28e76e1fdfSKyungsik Lee * from the binary stream which is generated by the preliminary 29e76e1fdfSKyungsik Lee * version of LZ4 tool so far. 30e76e1fdfSKyungsik Lee */ 31e76e1fdfSKyungsik Lee #define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20) 32e76e1fdfSKyungsik Lee #define ARCHIVE_MAGICNUMBER 0x184C2102 33e76e1fdfSKyungsik Lee 34*d97b07c5SYinghai Lu STATIC inline int INIT unlz4(u8 *input, long in_len, 35*d97b07c5SYinghai Lu long (*fill)(void *, unsigned long), 36*d97b07c5SYinghai Lu long (*flush)(void *, unsigned long), 37*d97b07c5SYinghai Lu u8 *output, long *posp, 38e76e1fdfSKyungsik Lee void (*error) (char *x)) 39e76e1fdfSKyungsik Lee { 40e76e1fdfSKyungsik Lee int ret = -1; 41e76e1fdfSKyungsik Lee size_t chunksize = 0; 42e76e1fdfSKyungsik Lee size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE; 43e76e1fdfSKyungsik Lee u8 *inp; 44e76e1fdfSKyungsik Lee u8 *inp_start; 45e76e1fdfSKyungsik Lee u8 *outp; 46*d97b07c5SYinghai Lu long size = in_len; 47e76e1fdfSKyungsik Lee #ifdef PREBOOT 48e76e1fdfSKyungsik Lee size_t out_len = get_unaligned_le32(input + in_len); 49e76e1fdfSKyungsik Lee #endif 50e76e1fdfSKyungsik Lee size_t dest_len; 51e76e1fdfSKyungsik Lee 52e76e1fdfSKyungsik Lee 53e76e1fdfSKyungsik Lee if (output) { 54e76e1fdfSKyungsik Lee outp = output; 55e76e1fdfSKyungsik Lee } else if (!flush) { 56e76e1fdfSKyungsik Lee error("NULL output pointer and no flush function provided"); 57e76e1fdfSKyungsik Lee goto exit_0; 58e76e1fdfSKyungsik Lee } else { 59e76e1fdfSKyungsik Lee outp = large_malloc(uncomp_chunksize); 60e76e1fdfSKyungsik Lee if (!outp) { 61e76e1fdfSKyungsik Lee error("Could not allocate output buffer"); 62e76e1fdfSKyungsik Lee goto exit_0; 63e76e1fdfSKyungsik Lee } 64e76e1fdfSKyungsik Lee } 65e76e1fdfSKyungsik Lee 66e76e1fdfSKyungsik Lee if (input && fill) { 67e76e1fdfSKyungsik Lee error("Both input pointer and fill function provided,"); 68e76e1fdfSKyungsik Lee goto exit_1; 69e76e1fdfSKyungsik Lee } else if (input) { 70e76e1fdfSKyungsik Lee inp = input; 71e76e1fdfSKyungsik Lee } else if (!fill) { 72e76e1fdfSKyungsik Lee error("NULL input pointer and missing fill function"); 73e76e1fdfSKyungsik Lee goto exit_1; 74e76e1fdfSKyungsik Lee } else { 75e76e1fdfSKyungsik Lee inp = large_malloc(lz4_compressbound(uncomp_chunksize)); 76e76e1fdfSKyungsik Lee if (!inp) { 77e76e1fdfSKyungsik Lee error("Could not allocate input buffer"); 78e76e1fdfSKyungsik Lee goto exit_1; 79e76e1fdfSKyungsik Lee } 80e76e1fdfSKyungsik Lee } 81e76e1fdfSKyungsik Lee inp_start = inp; 82e76e1fdfSKyungsik Lee 83e76e1fdfSKyungsik Lee if (posp) 84e76e1fdfSKyungsik Lee *posp = 0; 85e76e1fdfSKyungsik Lee 864d4b866aSYinghai Lu if (fill) { 874d4b866aSYinghai Lu size = fill(inp, 4); 884d4b866aSYinghai Lu if (size < 4) { 894d4b866aSYinghai Lu error("data corrupted"); 904d4b866aSYinghai Lu goto exit_2; 914d4b866aSYinghai Lu } 924d4b866aSYinghai Lu } 93e76e1fdfSKyungsik Lee 94e76e1fdfSKyungsik Lee chunksize = get_unaligned_le32(inp); 95e76e1fdfSKyungsik Lee if (chunksize == ARCHIVE_MAGICNUMBER) { 964d4b866aSYinghai Lu if (!fill) { 97e76e1fdfSKyungsik Lee inp += 4; 98e76e1fdfSKyungsik Lee size -= 4; 994d4b866aSYinghai Lu } 100e76e1fdfSKyungsik Lee } else { 101e76e1fdfSKyungsik Lee error("invalid header"); 102e76e1fdfSKyungsik Lee goto exit_2; 103e76e1fdfSKyungsik Lee } 104e76e1fdfSKyungsik Lee 105e76e1fdfSKyungsik Lee if (posp) 106e76e1fdfSKyungsik Lee *posp += 4; 107e76e1fdfSKyungsik Lee 108e76e1fdfSKyungsik Lee for (;;) { 109e76e1fdfSKyungsik Lee 1104d4b866aSYinghai Lu if (fill) { 1114d4b866aSYinghai Lu size = fill(inp, 4); 1124d4b866aSYinghai Lu if (size == 0) 1134d4b866aSYinghai Lu break; 1144d4b866aSYinghai Lu if (size < 4) { 1154d4b866aSYinghai Lu error("data corrupted"); 1164d4b866aSYinghai Lu goto exit_2; 1174d4b866aSYinghai Lu } 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 131e76e1fdfSKyungsik Lee 132e76e1fdfSKyungsik Lee if (posp) 133e76e1fdfSKyungsik Lee *posp += 4; 134e76e1fdfSKyungsik Lee 1354d4b866aSYinghai Lu if (!fill) { 1364d4b866aSYinghai Lu inp += 4; 1374d4b866aSYinghai Lu size -= 4; 1384d4b866aSYinghai Lu } else { 139e76e1fdfSKyungsik Lee if (chunksize > lz4_compressbound(uncomp_chunksize)) { 140e76e1fdfSKyungsik Lee error("chunk length is longer than allocated"); 141e76e1fdfSKyungsik Lee goto exit_2; 142e76e1fdfSKyungsik Lee } 1434d4b866aSYinghai Lu size = fill(inp, chunksize); 1444d4b866aSYinghai Lu if (size < chunksize) { 1454d4b866aSYinghai Lu error("data corrupted"); 1464d4b866aSYinghai Lu goto exit_2; 1474d4b866aSYinghai Lu } 148e76e1fdfSKyungsik Lee } 149e76e1fdfSKyungsik Lee #ifdef PREBOOT 150e76e1fdfSKyungsik Lee if (out_len >= uncomp_chunksize) { 151e76e1fdfSKyungsik Lee dest_len = uncomp_chunksize; 152e76e1fdfSKyungsik Lee out_len -= dest_len; 153e76e1fdfSKyungsik Lee } else 154e76e1fdfSKyungsik Lee dest_len = out_len; 155e76e1fdfSKyungsik Lee ret = lz4_decompress(inp, &chunksize, outp, dest_len); 156e76e1fdfSKyungsik Lee #else 157e76e1fdfSKyungsik Lee dest_len = uncomp_chunksize; 158e76e1fdfSKyungsik Lee ret = lz4_decompress_unknownoutputsize(inp, chunksize, outp, 159e76e1fdfSKyungsik Lee &dest_len); 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 199*d97b07c5SYinghai Lu STATIC int INIT decompress(unsigned char *buf, long in_len, 200*d97b07c5SYinghai Lu long (*fill)(void*, unsigned long), 201*d97b07c5SYinghai Lu long (*flush)(void*, unsigned long), 202e76e1fdfSKyungsik Lee unsigned char *output, 203*d97b07c5SYinghai 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