2013-07-09 03:01:46 +04:00
/*
* Wrapper for decompressing LZ4 - compressed kernel , initramfs , and initrd
*
* Copyright ( C ) 2013 , LG Electronics , Kyungsik Lee < kyungsik . lee @ lge . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# ifdef STATIC
# define PREBOOT
# include "lz4/lz4_decompress.c"
# else
# include <linux/decompress/unlz4.h>
# endif
# include <linux/types.h>
# include <linux/lz4.h>
# include <linux/decompress/mm.h>
# include <linux/compiler.h>
# include <asm/unaligned.h>
/*
* Note : Uncompressed chunk size is used in the compressor side
* ( userspace side for compression ) .
* It is hardcoded because there is not proper way to extract it
* from the binary stream which is generated by the preliminary
* version of LZ4 tool so far .
*/
# define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20)
# define ARCHIVE_MAGICNUMBER 0x184C2102
STATIC inline int INIT unlz4 ( u8 * input , int in_len ,
int ( * fill ) ( void * , unsigned int ) ,
int ( * flush ) ( void * , unsigned int ) ,
u8 * output , int * posp ,
void ( * error ) ( char * x ) )
{
int ret = - 1 ;
size_t chunksize = 0 ;
size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE ;
u8 * inp ;
u8 * inp_start ;
u8 * outp ;
int size = in_len ;
# ifdef PREBOOT
size_t out_len = get_unaligned_le32 ( input + in_len ) ;
# endif
size_t dest_len ;
if ( output ) {
outp = output ;
} else if ( ! flush ) {
error ( " NULL output pointer and no flush function provided " ) ;
goto exit_0 ;
} else {
outp = large_malloc ( uncomp_chunksize ) ;
if ( ! outp ) {
error ( " Could not allocate output buffer " ) ;
goto exit_0 ;
}
}
if ( input & & fill ) {
error ( " Both input pointer and fill function provided, " ) ;
goto exit_1 ;
} else if ( input ) {
inp = input ;
} else if ( ! fill ) {
error ( " NULL input pointer and missing fill function " ) ;
goto exit_1 ;
} else {
inp = large_malloc ( lz4_compressbound ( uncomp_chunksize ) ) ;
if ( ! inp ) {
error ( " Could not allocate input buffer " ) ;
goto exit_1 ;
}
}
inp_start = inp ;
if ( posp )
* posp = 0 ;
if ( fill )
fill ( inp , 4 ) ;
chunksize = get_unaligned_le32 ( inp ) ;
if ( chunksize = = ARCHIVE_MAGICNUMBER ) {
inp + = 4 ;
size - = 4 ;
} else {
error ( " invalid header " ) ;
goto exit_2 ;
}
if ( posp )
* posp + = 4 ;
for ( ; ; ) {
if ( fill )
fill ( inp , 4 ) ;
chunksize = get_unaligned_le32 ( inp ) ;
if ( chunksize = = ARCHIVE_MAGICNUMBER ) {
inp + = 4 ;
size - = 4 ;
if ( posp )
* posp + = 4 ;
continue ;
}
inp + = 4 ;
size - = 4 ;
if ( posp )
* posp + = 4 ;
if ( fill ) {
if ( chunksize > lz4_compressbound ( uncomp_chunksize ) ) {
error ( " chunk length is longer than allocated " ) ;
goto exit_2 ;
}
fill ( inp , chunksize ) ;
}
# ifdef PREBOOT
if ( out_len > = uncomp_chunksize ) {
dest_len = uncomp_chunksize ;
out_len - = dest_len ;
} else
dest_len = out_len ;
ret = lz4_decompress ( inp , & chunksize , outp , dest_len ) ;
# else
dest_len = uncomp_chunksize ;
ret = lz4_decompress_unknownoutputsize ( inp , chunksize , outp ,
& dest_len ) ;
# endif
if ( ret < 0 ) {
error ( " Decoding failed " ) ;
goto exit_2 ;
}
2014-01-24 03:56:20 +04:00
ret = - 1 ;
2013-07-09 03:01:46 +04:00
if ( flush & & flush ( outp , dest_len ) ! = dest_len )
goto exit_2 ;
if ( output )
outp + = dest_len ;
if ( posp )
* posp + = chunksize ;
size - = chunksize ;
if ( size = = 0 )
break ;
else if ( size < 0 ) {
error ( " data corrupted " ) ;
goto exit_2 ;
}
inp + = chunksize ;
if ( fill )
inp = inp_start ;
}
ret = 0 ;
exit_2 :
if ( ! input )
large_free ( inp_start ) ;
exit_1 :
if ( ! output )
large_free ( outp ) ;
exit_0 :
return ret ;
}
# ifdef PREBOOT
STATIC int INIT decompress ( unsigned char * buf , int in_len ,
int ( * fill ) ( void * , unsigned int ) ,
int ( * flush ) ( void * , unsigned int ) ,
unsigned char * output ,
int * posp ,
void ( * error ) ( char * x )
)
{
return unlz4 ( buf , in_len - 4 , fill , flush , output , posp , error ) ;
}
# endif