2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-05-07 20:49:14 +03:00
/*
* 842 Software Decompression
*
* Copyright ( C ) 2015 Dan Streetman , IBM Corp
*
* See 842. h for details of the 842 compressed format .
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# define MODULE_NAME "842_decompress"
# include "842.h"
# include "842_debugfs.h"
/* rolling fifo sizes */
# define I2_FIFO_SIZE (2 * (1 << I2_BITS))
# define I4_FIFO_SIZE (4 * (1 << I4_BITS))
# define I8_FIFO_SIZE (8 * (1 << I8_BITS))
static u8 decomp_ops [ OPS_MAX ] [ 4 ] = {
{ D8 , N0 , N0 , N0 } ,
{ D4 , D2 , I2 , N0 } ,
{ D4 , I2 , D2 , N0 } ,
{ D4 , I2 , I2 , N0 } ,
{ D4 , I4 , N0 , N0 } ,
{ D2 , I2 , D4 , N0 } ,
{ D2 , I2 , D2 , I2 } ,
{ D2 , I2 , I2 , D2 } ,
{ D2 , I2 , I2 , I2 } ,
{ D2 , I2 , I4 , N0 } ,
{ I2 , D2 , D4 , N0 } ,
{ I2 , D4 , I2 , N0 } ,
{ I2 , D2 , I2 , D2 } ,
{ I2 , D2 , I2 , I2 } ,
{ I2 , D2 , I4 , N0 } ,
{ I2 , I2 , D4 , N0 } ,
{ I2 , I2 , D2 , I2 } ,
{ I2 , I2 , I2 , D2 } ,
{ I2 , I2 , I2 , I2 } ,
{ I2 , I2 , I4 , N0 } ,
{ I4 , D4 , N0 , N0 } ,
{ I4 , D2 , I2 , N0 } ,
{ I4 , I2 , D2 , N0 } ,
{ I4 , I2 , I2 , N0 } ,
{ I4 , I4 , N0 , N0 } ,
{ I8 , N0 , N0 , N0 }
} ;
struct sw842_param {
u8 * in ;
u8 bit ;
u64 ilen ;
u8 * out ;
u8 * ostart ;
u64 olen ;
} ;
# define beN_to_cpu(d, s) \
( ( s ) = = 2 ? be16_to_cpu ( get_unaligned ( ( __be16 * ) d ) ) : \
( s ) = = 4 ? be32_to_cpu ( get_unaligned ( ( __be32 * ) d ) ) : \
( s ) = = 8 ? be64_to_cpu ( get_unaligned ( ( __be64 * ) d ) ) : \
2015-12-21 20:52:10 +03:00
0 )
2015-05-07 20:49:14 +03:00
static int next_bits ( struct sw842_param * p , u64 * d , u8 n ) ;
static int __split_next_bits ( struct sw842_param * p , u64 * d , u8 n , u8 s )
{
u64 tmp = 0 ;
int ret ;
if ( n < = s ) {
pr_debug ( " split_next_bits invalid n %u s %u \n " , n , s ) ;
return - EINVAL ;
}
ret = next_bits ( p , & tmp , n - s ) ;
if ( ret )
return ret ;
ret = next_bits ( p , d , s ) ;
if ( ret )
return ret ;
* d | = tmp < < s ;
return 0 ;
}
static int next_bits ( struct sw842_param * p , u64 * d , u8 n )
{
u8 * in = p - > in , b = p - > bit , bits = b + n ;
if ( n > 64 ) {
pr_debug ( " next_bits invalid n %u \n " , n ) ;
return - EINVAL ;
}
/* split this up if reading > 8 bytes, or if we're at the end of
* the input buffer and would read past the end
*/
if ( bits > 64 )
return __split_next_bits ( p , d , n , 32 ) ;
else if ( p - > ilen < 8 & & bits > 32 & & bits < = 56 )
return __split_next_bits ( p , d , n , 16 ) ;
else if ( p - > ilen < 4 & & bits > 16 & & bits < = 24 )
return __split_next_bits ( p , d , n , 8 ) ;
if ( DIV_ROUND_UP ( bits , 8 ) > p - > ilen )
return - EOVERFLOW ;
if ( bits < = 8 )
* d = * in > > ( 8 - bits ) ;
else if ( bits < = 16 )
* d = be16_to_cpu ( get_unaligned ( ( __be16 * ) in ) ) > > ( 16 - bits ) ;
else if ( bits < = 32 )
* d = be32_to_cpu ( get_unaligned ( ( __be32 * ) in ) ) > > ( 32 - bits ) ;
else
* d = be64_to_cpu ( get_unaligned ( ( __be64 * ) in ) ) > > ( 64 - bits ) ;
* d & = GENMASK_ULL ( n - 1 , 0 ) ;
p - > bit + = n ;
if ( p - > bit > 7 ) {
p - > in + = p - > bit / 8 ;
p - > ilen - = p - > bit / 8 ;
p - > bit % = 8 ;
}
return 0 ;
}
static int do_data ( struct sw842_param * p , u8 n )
{
u64 v ;
int ret ;
if ( n > p - > olen )
return - ENOSPC ;
ret = next_bits ( p , & v , n * 8 ) ;
if ( ret )
return ret ;
switch ( n ) {
case 2 :
put_unaligned ( cpu_to_be16 ( ( u16 ) v ) , ( __be16 * ) p - > out ) ;
break ;
case 4 :
put_unaligned ( cpu_to_be32 ( ( u32 ) v ) , ( __be32 * ) p - > out ) ;
break ;
case 8 :
put_unaligned ( cpu_to_be64 ( ( u64 ) v ) , ( __be64 * ) p - > out ) ;
break ;
default :
return - EINVAL ;
}
p - > out + = n ;
p - > olen - = n ;
return 0 ;
}
static int __do_index ( struct sw842_param * p , u8 size , u8 bits , u64 fsize )
{
u64 index , offset , total = round_down ( p - > out - p - > ostart , 8 ) ;
int ret ;
ret = next_bits ( p , & index , bits ) ;
if ( ret )
return ret ;
offset = index * size ;
/* a ring buffer of fsize is used; correct the offset */
if ( total > fsize ) {
/* this is where the current fifo is */
u64 section = round_down ( total , fsize ) ;
/* the current pos in the fifo */
2015-05-12 01:53:36 +03:00
u64 pos = total - section ;
2015-05-07 20:49:14 +03:00
/* if the offset is past/at the pos, we need to
* go back to the last fifo section
*/
if ( offset > = pos )
section - = fsize ;
offset + = section ;
}
if ( offset + size > total ) {
pr_debug ( " index%x %lx points past end %lx \n " , size ,
( unsigned long ) offset , ( unsigned long ) total ) ;
return - EINVAL ;
}
2015-12-21 20:52:10 +03:00
if ( size ! = 2 & & size ! = 4 & & size ! = 8 )
WARN ( 1 , " __do_index invalid size %x \n " , size ) ;
else
pr_debug ( " index%x to %lx off %lx adjoff %lx tot %lx data %lx \n " ,
size , ( unsigned long ) index ,
( unsigned long ) ( index * size ) , ( unsigned long ) offset ,
( unsigned long ) total ,
( unsigned long ) beN_to_cpu ( & p - > ostart [ offset ] , size ) ) ;
2015-05-07 20:49:14 +03:00
memcpy ( p - > out , & p - > ostart [ offset ] , size ) ;
p - > out + = size ;
p - > olen - = size ;
return 0 ;
}
2015-05-11 14:22:35 +03:00
static int do_index ( struct sw842_param * p , u8 n )
2015-05-07 20:49:14 +03:00
{
switch ( n ) {
case 2 :
return __do_index ( p , 2 , I2_BITS , I2_FIFO_SIZE ) ;
case 4 :
return __do_index ( p , 4 , I4_BITS , I4_FIFO_SIZE ) ;
case 8 :
return __do_index ( p , 8 , I8_BITS , I8_FIFO_SIZE ) ;
default :
return - EINVAL ;
}
}
2015-05-11 14:22:35 +03:00
static int do_op ( struct sw842_param * p , u8 o )
2015-05-07 20:49:14 +03:00
{
int i , ret = 0 ;
if ( o > = OPS_MAX )
return - EINVAL ;
for ( i = 0 ; i < 4 ; i + + ) {
u8 op = decomp_ops [ o ] [ i ] ;
pr_debug ( " op is %x \n " , op ) ;
switch ( op & OP_ACTION ) {
case OP_ACTION_DATA :
ret = do_data ( p , op & OP_AMOUNT ) ;
break ;
case OP_ACTION_INDEX :
ret = do_index ( p , op & OP_AMOUNT ) ;
break ;
case OP_ACTION_NOOP :
break ;
default :
2015-12-13 09:26:11 +03:00
pr_err ( " Internal error, invalid op %x \n " , op ) ;
2015-05-07 20:49:14 +03:00
return - EINVAL ;
}
if ( ret )
return ret ;
}
if ( sw842_template_counts )
atomic_inc ( & template_count [ o ] ) ;
return 0 ;
}
/**
* sw842_decompress
*
* Decompress the 842 - compressed buffer of length @ ilen at @ in
* to the output buffer @ out , using no more than @ olen bytes .
*
* The compressed buffer must be only a single 842 - compressed buffer ,
* with the standard format described in the comments in 842. h
* Processing will stop when the 842 " END " template is detected ,
* not the end of the buffer .
*
* Returns : 0 on success , error on failure . The @ olen parameter
* will contain the number of output bytes written on success , or
* 0 on error .
*/
int sw842_decompress ( const u8 * in , unsigned int ilen ,
u8 * out , unsigned int * olen )
{
struct sw842_param p ;
int ret ;
u64 op , rep , tmp , bytes , total ;
2015-10-08 23:45:51 +03:00
u64 crc ;
2015-05-07 20:49:14 +03:00
p . in = ( u8 * ) in ;
p . bit = 0 ;
p . ilen = ilen ;
p . out = out ;
p . ostart = out ;
p . olen = * olen ;
total = p . olen ;
* olen = 0 ;
do {
ret = next_bits ( & p , & op , OP_BITS ) ;
if ( ret )
return ret ;
pr_debug ( " template is %lx \n " , ( unsigned long ) op ) ;
switch ( op ) {
case OP_REPEAT :
ret = next_bits ( & p , & rep , REPEAT_BITS ) ;
if ( ret )
return ret ;
if ( p . out = = out ) /* no previous bytes */
return - EINVAL ;
/* copy rep + 1 */
rep + + ;
if ( rep * 8 > p . olen )
return - ENOSPC ;
while ( rep - - > 0 ) {
memcpy ( p . out , p . out - 8 , 8 ) ;
p . out + = 8 ;
p . olen - = 8 ;
}
if ( sw842_template_counts )
atomic_inc ( & template_repeat_count ) ;
break ;
case OP_ZEROS :
if ( 8 > p . olen )
return - ENOSPC ;
memset ( p . out , 0 , 8 ) ;
p . out + = 8 ;
p . olen - = 8 ;
if ( sw842_template_counts )
atomic_inc ( & template_zeros_count ) ;
break ;
case OP_SHORT_DATA :
ret = next_bits ( & p , & bytes , SHORT_DATA_BITS ) ;
if ( ret )
return ret ;
if ( ! bytes | | bytes > SHORT_DATA_BITS_MAX )
return - EINVAL ;
while ( bytes - - > 0 ) {
ret = next_bits ( & p , & tmp , 8 ) ;
if ( ret )
return ret ;
* p . out = ( u8 ) tmp ;
p . out + + ;
p . olen - - ;
}
if ( sw842_template_counts )
atomic_inc ( & template_short_data_count ) ;
break ;
case OP_END :
if ( sw842_template_counts )
atomic_inc ( & template_end_count ) ;
break ;
default : /* use template */
ret = do_op ( & p , op ) ;
if ( ret )
return ret ;
break ;
}
} while ( op ! = OP_END ) ;
2015-10-08 23:45:51 +03:00
/*
* crc ( 0 : 31 ) is saved in compressed data starting with the
* next bit after End of stream template .
*/
ret = next_bits ( & p , & crc , CRC_BITS ) ;
if ( ret )
return ret ;
/*
* Validate CRC saved in compressed data .
*/
if ( crc ! = ( u64 ) crc32_be ( 0 , out , total - p . olen ) ) {
pr_debug ( " CRC mismatch for decompression \n " ) ;
return - EINVAL ;
}
2015-05-07 20:49:14 +03:00
if ( unlikely ( ( total - p . olen ) > UINT_MAX ) )
return - ENOSPC ;
* olen = total - p . olen ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( sw842_decompress ) ;
static int __init sw842_init ( void )
{
if ( sw842_template_counts )
sw842_debugfs_create ( ) ;
return 0 ;
}
module_init ( sw842_init ) ;
static void __exit sw842_exit ( void )
{
if ( sw842_template_counts )
sw842_debugfs_remove ( ) ;
}
module_exit ( sw842_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " Software 842 Decompressor " ) ;
MODULE_AUTHOR ( " Dan Streetman <ddstreet@ieee.org> " ) ;