2016-09-22 09:54:31 +03:00
/*
* Wrapper around the kernel ' s pre - boot decompression library .
*
* Copyright ( C ) IBM Corporation 2016.
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version
* 2 of the License , or ( at your option ) any later version .
*/
# include "elf.h"
# include "page.h"
# include "string.h"
# include "stdio.h"
# include "ops.h"
# include "reg.h"
# include "types.h"
/*
* The decompressor_ * . c files play # ifdef games so they can be used in both
* pre - boot and regular kernel code . We need these definitions to make the
* includes work .
*/
# define STATIC static
# define INIT
# define __always_inline inline
/*
* The build process will copy the required zlib source files and headers
* out of lib / and " fix " the includes so they do not pull in other kernel
* headers .
*/
# ifdef CONFIG_KERNEL_GZIP
# include "decompress_inflate.c"
# endif
2016-09-22 09:54:34 +03:00
# ifdef CONFIG_KERNEL_XZ
# include "xz_config.h"
# include ".. / .. / .. / lib / decompress_unxz.c"
# endif
2016-09-22 09:54:31 +03:00
/* globals for tracking the state of the decompression */
static unsigned long decompressed_bytes ;
static unsigned long limit ;
static unsigned long skip ;
static char * output_buffer ;
/*
* flush ( ) is called by __decompress ( ) when the decompressor ' s scratch buffer is
* full .
*/
static long flush ( void * v , unsigned long buffer_size )
{
unsigned long end = decompressed_bytes + buffer_size ;
unsigned long size = buffer_size ;
unsigned long offset = 0 ;
char * in = v ;
char * out ;
/*
* if we hit our decompression limit , we need to fake an error to abort
* the in - progress decompression .
*/
if ( decompressed_bytes > = limit )
return - 1 ;
/* skip this entire block */
if ( end < = skip ) {
decompressed_bytes + = buffer_size ;
return buffer_size ;
}
/* skip some data at the start, but keep the rest of the block */
if ( decompressed_bytes < skip & & end > skip ) {
offset = skip - decompressed_bytes ;
in + = offset ;
size - = offset ;
decompressed_bytes + = offset ;
}
out = & output_buffer [ decompressed_bytes - skip ] ;
size = min ( decompressed_bytes + size , limit ) - decompressed_bytes ;
memcpy ( out , in , size ) ;
decompressed_bytes + = size ;
return buffer_size ;
}
static void print_err ( char * s )
{
/* suppress the "error" when we terminate the decompressor */
if ( decompressed_bytes > = limit )
return ;
printf ( " Decompression error: '%s' \n \r " , s ) ;
}
/**
* partial_decompress - decompresses part or all of a compressed buffer
* @ inbuf : input buffer
* @ input_size : length of the input buffer
* @ outbuf : input buffer
* @ output_size : length of the input buffer
* @ skip number of output bytes to ignore
*
* This function takes compressed data from inbuf , decompresses and write it to
* outbuf . Once output_size bytes are written to the output buffer , or the
* stream is exhausted the function will return the number of bytes that were
* decompressed . Otherwise it will return whatever error code the decompressor
* reported ( NB : This is specific to each decompressor type ) .
*
* The skip functionality is mainly there so the program and discover
* the size of the compressed image so that it can ask firmware ( if present )
* for an appropriately sized buffer .
*/
long partial_decompress ( void * inbuf , unsigned long input_size ,
void * outbuf , unsigned long output_size , unsigned long _skip )
{
int ret ;
/*
* The skipped bytes needs to be included in the size of data we want
* to decompress .
*/
output_size + = _skip ;
decompressed_bytes = 0 ;
output_buffer = outbuf ;
limit = output_size ;
skip = _skip ;
ret = __decompress ( inbuf , input_size , NULL , flush , outbuf ,
output_size , NULL , print_err ) ;
/*
* If decompression was aborted due to an actual error rather than
* a fake error that we used to abort , then we should report it .
*/
if ( decompressed_bytes < limit )
return ret ;
return decompressed_bytes - skip ;
}