decompressor: add LZ4 decompressor module

Add support for LZ4 decompression in the Linux Kernel.  LZ4 Decompression
APIs for kernel are based on LZ4 implementation by Yann Collet.

Benchmark Results(PATCH v3)
Compiler: Linaro ARM gcc 4.6.2

1. ARMv7, 1.5GHz based board
   Kernel: linux 3.4
   Uncompressed Kernel Size: 14MB
        Compressed Size  Decompression Speed
   LZO  6.7MB            20.1MB/s, 25.2MB/s(UA)
   LZ4  7.3MB            29.1MB/s, 45.6MB/s(UA)

2. ARMv7, 1.7GHz based board
   Kernel: linux 3.7
   Uncompressed Kernel Size: 14MB
        Compressed Size  Decompression Speed
   LZO  6.0MB            34.1MB/s, 52.2MB/s(UA)
   LZ4  6.5MB            86.7MB/s
- UA: Unaligned memory Access support
- Latest patch set for LZO applied

This patch set is for adding support for LZ4-compressed Kernel.  LZ4 is a
very fast lossless compression algorithm and it also features an extremely
fast decoder [1].

But we have five of decompressors already and one question which does
arise, however, is that of where do we stop adding new ones?  This issue
had been discussed and came to the conclusion [2].

Russell King said that we should have:

 - one decompressor which is the fastest
 - one decompressor for the highest compression ratio
 - one popular decompressor (eg conventional gzip)

If we have a replacement one for one of these, then it should do exactly
that: replace it.

The benchmark shows that an 8% increase in image size vs a 66% increase
in decompression speed compared to LZO(which has been known as the
fastest decompressor in the Kernel).  Therefore the "fast but may not be
small" compression title has clearly been taken by LZ4 [3].

[1] http://code.google.com/p/lz4/
[2] http://thread.gmane.org/gmane.linux.kbuild.devel/9157
[3] http://thread.gmane.org/gmane.linux.kbuild.devel/9347

LZ4 homepage: http://fastcompression.blogspot.com/p/lz4.html
LZ4 source repository: http://code.google.com/p/lz4/

Signed-off-by: Kyungsik Lee <kyungsik.lee@lge.com>
Signed-off-by: Yann Collet <yann.collet.73@gmail.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Florian Fainelli <florian@openwrt.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

lib: add support for LZ4-compressed kernel

Add support for extracting LZ4-compressed kernel images, as well as
LZ4-compressed ramdisk images in the kernel boot process.

Signed-off-by: Kyungsik Lee <kyungsik.lee@lge.com>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Russell King <rmk@arm.linux.org.uk>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Florian Fainelli <florian@openwrt.org>
Cc: Yann Collet <yann.collet.73@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

lib: add lz4 compressor module

This patchset is for supporting LZ4 compression and the crypto API using
it.

As shown below, the size of data is a little bit bigger but compressing
speed is faster under the enabled unaligned memory access.  We can use
lz4 de/compression through crypto API as well.  Also, It will be useful
for another potential user of lz4 compression.

lz4 Compression Benchmark:
Compiler: ARM gcc 4.6.4
ARMv7, 1 GHz based board
   Kernel: linux 3.4
   Uncompressed data Size: 101 MB
         Compressed Size  compression Speed
   LZO   72.1MB		  32.1MB/s, 33.0MB/s(UA)
   LZ4   75.1MB		  30.4MB/s, 35.9MB/s(UA)
   LZ4HC 59.8MB		   2.4MB/s,  2.5MB/s(UA)
- UA: Unaligned memory Access support
- Latest patch set for LZO applied

This patch:

Add support for LZ4 compression in the Linux Kernel.  LZ4 Compression APIs
for kernel are based on LZ4 implementation by Yann Collet and were changed
for kernel coding style.

LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
LZ4 source repository : http://code.google.com/p/lz4/
svn revision : r90

Two APIs are added:

lz4_compress() support basic lz4 compression whereas lz4hc_compress()
support high compression or CPU performance get lower but compression
ratio get higher.  Also, we require the pre-allocated working memory with
the defined size and destination buffer must be allocated with the size of
lz4_compressbound.

[akpm@linux-foundation.org: make lz4_compresshcctx() static]
Signed-off-by: Chanho Min <chanho.min@lge.com>
Cc: "Darrick J. Wong" <djwong@us.ibm.com>
Cc: Bob Pearson <rpearson@systemfabricworks.com>
Cc: Richard Weinberger <richard@nod.at>
Cc: Herbert Xu <herbert@gondor.hengli.com.au>
Cc: Yann Collet <yann.collet.73@gmail.com>
Cc: Kyungsik Lee <kyungsik.lee@lge.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

lib/lz4: correct the LZ4 license

The LZ4 code is listed as using the "BSD 2-Clause License".

Signed-off-by: Richard Laager <rlaager@wiktel.com>
Acked-by: Kyungsik Lee <kyungsik.lee@lge.com>
Cc: Chanho Min <chanho.min@lge.com>
Cc: Richard Yao <ryao@gentoo.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
[ The 2-clause BSD can be just converted into GPL, but that's rude and
  pointless, so don't do it   - Linus ]
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

lz4: fix compression/decompression signedness mismatch

LZ4 compression and decompression functions require different in
signedness input/output parameters: unsigned char for compression and
signed char for decompression.

Change decompression API to require "(const) unsigned char *".

Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Cc: Kyungsik Lee <kyungsik.lee@lge.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Yann Collet <yann.collet.73@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

lz4: ensure length does not wrap

Given some pathologically compressed data, lz4 could possibly decide to
wrap a few internal variables, causing unknown things to happen.  Catch
this before the wrapping happens and abort the decompression.

Reported-by: "Don A. Bailey" <donb@securitymouse.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

lz4: fix another possible overrun

There is one other possible overrun in the lz4 code as implemented by
Linux at this point in time (which differs from the upstream lz4
codebase, but will get synced at in a future kernel release.)  As
pointed out by Don, we also need to check the overflow in the data
itself.

While we are at it, replace the odd error return value with just a
"simple" -1 value as the return value is never used for anything other
than a basic "did this work or not" check.

Reported-by: "Don A. Bailey" <donb@securitymouse.com>
Reported-by: Willy Tarreau <w@1wt.eu>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

lz4: add overrun checks to lz4_uncompress_unknownoutputsize()

Jan points out that I forgot to make the needed fixes to the
lz4_uncompress_unknownoutputsize() function to mirror the changes done
in lz4_decompress() with regards to potential pointer overflows.

The only in-kernel user of this function is the zram code, which only
takes data from a valid compressed buffer that it made itself, so it's
not a big issue.  But due to external kernel modules using this
function, it's better to be safe here.

Reported-by: Jan Beulich <JBeulich@suse.com>
Cc: "Don A. Bailey" <donb@securitymouse.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

LZ4 : fix the data abort issue

If the part of the compression data are corrupted, or the compression
data is totally fake, the memory access over the limit is possible.

This is the log from my system usning lz4 decompression.
   [6502]data abort, halting
   [6503]r0  0x00000000 r1  0x00000000 r2  0xdcea0ffc r3  0xdcea0ffc
   [6509]r4  0xb9ab0bfd r5  0xdcea0ffc r6  0xdcea0ff8 r7  0xdce80000
   [6515]r8  0x00000000 r9  0x00000000 r10 0x00000000 r11 0xb9a98000
   [6522]r12 0xdcea1000 usp 0x00000000 ulr 0x00000000 pc  0x820149bc
   [6528]spsr 0x400001f3
and the memory addresses of some variables at the moment are
    ref:0xdcea0ffc, op:0xdcea0ffc, oend:0xdcea1000

As you can see, COPYLENGH is 8bytes, so @ref and @op can access the momory
over @oend.

Change-Id: I9919c9bcfca9ae9e26d83fa49afff99e74295d3b
Signed-off-by: JeHyeon Yeon <tom.yeon@windriver.com>
Reviewed-by: David Sterba <dsterba@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org>
This commit is contained in:
Kyungsik Lee 2013-07-08 16:01:45 -07:00 committed by Francescodario Cuzzocrea
parent f442812b2c
commit e5bbc41c31
14 changed files with 1814 additions and 1 deletions

View File

@ -0,0 +1,10 @@
#ifndef DECOMPRESS_UNLZ4_H
#define DECOMPRESS_UNLZ4_H
int unlz4(unsigned char *inbuf, int len,
int(*fill)(void*, unsigned int),
int(*flush)(void*, unsigned int),
unsigned char *output,
int *pos,
void(*error)(char *x));
#endif

87
include/linux/lz4.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef __LZ4_H__
#define __LZ4_H__
/*
* LZ4 Kernel Interface
*
* 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.
*/
#define LZ4_MEM_COMPRESS (4096 * sizeof(unsigned char *))
#define LZ4HC_MEM_COMPRESS (65538 * sizeof(unsigned char *))
/*
* lz4_compressbound()
* Provides the maximum size that LZ4 may output in a "worst case" scenario
* (input data not compressible)
*/
static inline size_t lz4_compressbound(size_t isize)
{
return isize + (isize / 255) + 16;
}
/*
* lz4_compress()
* src : source address of the original data
* src_len : size of the original data
* dst : output buffer address of the compressed data
* This requires 'dst' of size LZ4_COMPRESSBOUND.
* dst_len : is the output size, which is returned after compress done
* workmem : address of the working memory.
* This requires 'workmem' of size LZ4_MEM_COMPRESS.
* return : Success if return 0
* Error if return (< 0)
* note : Destination buffer and workmem must be already allocated with
* the defined size.
*/
int lz4_compress(const unsigned char *src, size_t src_len,
unsigned char *dst, size_t *dst_len, void *wrkmem);
/*
* lz4hc_compress()
* src : source address of the original data
* src_len : size of the original data
* dst : output buffer address of the compressed data
* This requires 'dst' of size LZ4_COMPRESSBOUND.
* dst_len : is the output size, which is returned after compress done
* workmem : address of the working memory.
* This requires 'workmem' of size LZ4HC_MEM_COMPRESS.
* return : Success if return 0
* Error if return (< 0)
* note : Destination buffer and workmem must be already allocated with
* the defined size.
*/
int lz4hc_compress(const unsigned char *src, size_t src_len,
unsigned char *dst, size_t *dst_len, void *wrkmem);
/*
* lz4_decompress()
* src : source address of the compressed data
* src_len : is the input size, whcih is returned after decompress done
* dest : output buffer address of the decompressed data
* actual_dest_len: is the size of uncompressed data, supposing it's known
* return : Success if return 0
* Error if return (< 0)
* note : Destination buffer must be already allocated.
* slightly faster than lz4_decompress_unknownoutputsize()
*/
int lz4_decompress(const unsigned char *src, size_t *src_len,
unsigned char *dest, size_t actual_dest_len);
/*
* lz4_decompress_unknownoutputsize()
* src : source address of the compressed data
* src_len : is the input size, therefore the compressed size
* dest : output buffer address of the decompressed data
* dest_len: is the max size of the destination buffer, which is
* returned with actual size of decompressed data after
* decompress done
* return : Success if return 0
* Error if return (< 0)
* note : Destination buffer must be already allocated.
*/
int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len,
unsigned char *dest, size_t *dest_len);
#endif

View File

@ -130,10 +130,13 @@ config HAVE_KERNEL_XZ
config HAVE_KERNEL_LZO
bool
config HAVE_KERNEL_LZ4
bool
choice
prompt "Kernel compression mode"
default KERNEL_GZIP
depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO
depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO || HAVE_KERNEL_LZ4
help
The linux kernel is a kind of self-extracting executable.
Several compression algorithms are available, which differ
@ -201,6 +204,18 @@ config KERNEL_LZO
size is about 10% bigger than gzip; however its speed
(both compression and decompression) is the fastest.
config KERNEL_LZ4
bool "LZ4"
depends on HAVE_KERNEL_LZ4
help
LZ4 is an LZ77-type compressor with a fixed, byte-oriented encoding.
A preliminary version of LZ4 de/compression tool is available at
<https://code.google.com/p/lz4/>.
Its compression ratio is worse than LZO. The size of the kernel
is about 8% bigger than LZO. But the decompression speed is
faster than LZO.
endchoice
config DEFAULT_HOSTNAME

View File

@ -177,6 +177,15 @@ config LZO_COMPRESS
config LZO_DECOMPRESS
tristate
config LZ4_COMPRESS
tristate
config LZ4HC_COMPRESS
tristate
config LZ4_DECOMPRESS
tristate
source "lib/xz/Kconfig"
#
@ -201,6 +210,10 @@ config DECOMPRESS_LZO
select LZO_DECOMPRESS
tristate
config DECOMPRESS_LZ4
select LZ4_DECOMPRESS
tristate
#
# Generic allocator support is selected if needed
#

View File

@ -72,6 +72,9 @@ obj-$(CONFIG_REED_SOLOMON) += reed_solomon/
obj-$(CONFIG_BCH) += bch.o
obj-$(CONFIG_LZO_COMPRESS) += lzo/
obj-$(CONFIG_LZO_DECOMPRESS) += lzo/
obj-$(CONFIG_LZ4_COMPRESS) += lz4/
obj-$(CONFIG_LZ4HC_COMPRESS) += lz4/
obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/
obj-$(CONFIG_XZ_DEC) += xz/
obj-$(CONFIG_RAID6_PQ) += raid6/
@ -80,6 +83,7 @@ lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o
lib-$(CONFIG_DECOMPRESS_XZ) += decompress_unxz.o
lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o
lib-$(CONFIG_DECOMPRESS_LZ4) += decompress_unlz4.o
obj-$(CONFIG_TEXTSEARCH) += textsearch.o
obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o

View File

@ -11,6 +11,7 @@
#include <linux/decompress/unxz.h>
#include <linux/decompress/inflate.h>
#include <linux/decompress/unlzo.h>
#include <linux/decompress/unlz4.h>
#include <linux/types.h>
#include <linux/string.h>
@ -30,6 +31,9 @@
#ifndef CONFIG_DECOMPRESS_LZO
# define unlzo NULL
#endif
#ifndef CONFIG_DECOMPRESS_LZ4
# define unlz4 NULL
#endif
static const struct compress_format {
unsigned char magic[2];
@ -42,6 +46,7 @@ static const struct compress_format {
{ {0x5d, 0x00}, "lzma", unlzma },
{ {0xfd, 0x37}, "xz", unxz },
{ {0x89, 0x4c}, "lzo", unlzo },
{ {0x02, 0x21}, "lz4", unlz4 },
{ {0, 0}, NULL, NULL }
};

187
lib/decompress_unlz4.c Normal file
View File

@ -0,0 +1,187 @@
/*
* 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;
}
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

3
lib/lz4/Makefile Normal file
View File

@ -0,0 +1,3 @@
obj-$(CONFIG_LZ4_COMPRESS) += lz4_compress.o
obj-$(CONFIG_LZ4HC_COMPRESS) += lz4hc_compress.o
obj-$(CONFIG_LZ4_DECOMPRESS) += lz4_decompress.o

443
lib/lz4/lz4_compress.c Normal file
View File

@ -0,0 +1,443 @@
/*
* LZ4 - Fast LZ compression algorithm
* Copyright (C) 2011-2012, Yann Collet.
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at :
* - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
* - LZ4 source repository : http://code.google.com/p/lz4/
*
* Changed for kernel use by:
* Chanho Min <chanho.min@lge.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/lz4.h>
#include <asm/unaligned.h>
#include "lz4defs.h"
/*
* LZ4_compressCtx :
* -----------------
* Compress 'isize' bytes from 'source' into an output buffer 'dest' of
* maximum size 'maxOutputSize'. * If it cannot achieve it, compression
* will stop, and result of the function will be zero.
* return : the number of bytes written in buffer 'dest', or 0 if the
* compression fails
*/
static inline int lz4_compressctx(void *ctx,
const char *source,
char *dest,
int isize,
int maxoutputsize)
{
HTYPE *hashtable = (HTYPE *)ctx;
const u8 *ip = (u8 *)source;
#if LZ4_ARCH64
const BYTE * const base = ip;
#else
const int base = 0;
#endif
const u8 *anchor = ip;
const u8 *const iend = ip + isize;
const u8 *const mflimit = iend - MFLIMIT;
#define MATCHLIMIT (iend - LASTLITERALS)
u8 *op = (u8 *) dest;
u8 *const oend = op + maxoutputsize;
int length;
const int skipstrength = SKIPSTRENGTH;
u32 forwardh;
int lastrun;
/* Init */
if (isize < MINLENGTH)
goto _last_literals;
memset((void *)hashtable, 0, LZ4_MEM_COMPRESS);
/* First Byte */
hashtable[LZ4_HASH_VALUE(ip)] = ip - base;
ip++;
forwardh = LZ4_HASH_VALUE(ip);
/* Main Loop */
for (;;) {
int findmatchattempts = (1U << skipstrength) + 3;
const u8 *forwardip = ip;
const u8 *ref;
u8 *token;
/* Find a match */
do {
u32 h = forwardh;
int step = findmatchattempts++ >> skipstrength;
ip = forwardip;
forwardip = ip + step;
if (unlikely(forwardip > mflimit))
goto _last_literals;
forwardh = LZ4_HASH_VALUE(forwardip);
ref = base + hashtable[h];
hashtable[h] = ip - base;
} while ((ref < ip - MAX_DISTANCE) || (A32(ref) != A32(ip)));
/* Catch up */
while ((ip > anchor) && (ref > (u8 *)source) &&
unlikely(ip[-1] == ref[-1])) {
ip--;
ref--;
}
/* Encode Literal length */
length = (int)(ip - anchor);
token = op++;
/* check output limit */
if (unlikely(op + length + (2 + 1 + LASTLITERALS) +
(length >> 8) > oend))
return 0;
if (length >= (int)RUN_MASK) {
int len;
*token = (RUN_MASK << ML_BITS);
len = length - RUN_MASK;
for (; len > 254 ; len -= 255)
*op++ = 255;
*op++ = (u8)len;
} else
*token = (length << ML_BITS);
/* Copy Literals */
LZ4_BLINDCOPY(anchor, op, length);
_next_match:
/* Encode Offset */
LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref));
/* Start Counting */
ip += MINMATCH;
/* MinMatch verified */
ref += MINMATCH;
anchor = ip;
while (likely(ip < MATCHLIMIT - (STEPSIZE - 1))) {
#if LZ4_ARCH64
u64 diff = A64(ref) ^ A64(ip);
#else
u32 diff = A32(ref) ^ A32(ip);
#endif
if (!diff) {
ip += STEPSIZE;
ref += STEPSIZE;
continue;
}
ip += LZ4_NBCOMMONBYTES(diff);
goto _endcount;
}
#if LZ4_ARCH64
if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) {
ip += 4;
ref += 4;
}
#endif
if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) {
ip += 2;
ref += 2;
}
if ((ip < MATCHLIMIT) && (*ref == *ip))
ip++;
_endcount:
/* Encode MatchLength */
length = (int)(ip - anchor);
/* Check output limit */
if (unlikely(op + (1 + LASTLITERALS) + (length >> 8) > oend))
return 0;
if (length >= (int)ML_MASK) {
*token += ML_MASK;
length -= ML_MASK;
for (; length > 509 ; length -= 510) {
*op++ = 255;
*op++ = 255;
}
if (length > 254) {
length -= 255;
*op++ = 255;
}
*op++ = (u8)length;
} else
*token += length;
/* Test end of chunk */
if (ip > mflimit) {
anchor = ip;
break;
}
/* Fill table */
hashtable[LZ4_HASH_VALUE(ip-2)] = ip - 2 - base;
/* Test next position */
ref = base + hashtable[LZ4_HASH_VALUE(ip)];
hashtable[LZ4_HASH_VALUE(ip)] = ip - base;
if ((ref > ip - (MAX_DISTANCE + 1)) && (A32(ref) == A32(ip))) {
token = op++;
*token = 0;
goto _next_match;
}
/* Prepare next loop */
anchor = ip++;
forwardh = LZ4_HASH_VALUE(ip);
}
_last_literals:
/* Encode Last Literals */
lastrun = (int)(iend - anchor);
if (((char *)op - dest) + lastrun + 1
+ ((lastrun + 255 - RUN_MASK) / 255) > (u32)maxoutputsize)
return 0;
if (lastrun >= (int)RUN_MASK) {
*op++ = (RUN_MASK << ML_BITS);
lastrun -= RUN_MASK;
for (; lastrun > 254 ; lastrun -= 255)
*op++ = 255;
*op++ = (u8)lastrun;
} else
*op++ = (lastrun << ML_BITS);
memcpy(op, anchor, iend - anchor);
op += iend - anchor;
/* End */
return (int)(((char *)op) - dest);
}
static inline int lz4_compress64kctx(void *ctx,
const char *source,
char *dest,
int isize,
int maxoutputsize)
{
u16 *hashtable = (u16 *)ctx;
const u8 *ip = (u8 *) source;
const u8 *anchor = ip;
const u8 *const base = ip;
const u8 *const iend = ip + isize;
const u8 *const mflimit = iend - MFLIMIT;
#define MATCHLIMIT (iend - LASTLITERALS)
u8 *op = (u8 *) dest;
u8 *const oend = op + maxoutputsize;
int len, length;
const int skipstrength = SKIPSTRENGTH;
u32 forwardh;
int lastrun;
/* Init */
if (isize < MINLENGTH)
goto _last_literals;
memset((void *)hashtable, 0, LZ4_MEM_COMPRESS);
/* First Byte */
ip++;
forwardh = LZ4_HASH64K_VALUE(ip);
/* Main Loop */
for (;;) {
int findmatchattempts = (1U << skipstrength) + 3;
const u8 *forwardip = ip;
const u8 *ref;
u8 *token;
/* Find a match */
do {
u32 h = forwardh;
int step = findmatchattempts++ >> skipstrength;
ip = forwardip;
forwardip = ip + step;
if (forwardip > mflimit)
goto _last_literals;
forwardh = LZ4_HASH64K_VALUE(forwardip);
ref = base + hashtable[h];
hashtable[h] = (u16)(ip - base);
} while (A32(ref) != A32(ip));
/* Catch up */
while ((ip > anchor) && (ref > (u8 *)source)
&& (ip[-1] == ref[-1])) {
ip--;
ref--;
}
/* Encode Literal length */
length = (int)(ip - anchor);
token = op++;
/* Check output limit */
if (unlikely(op + length + (2 + 1 + LASTLITERALS)
+ (length >> 8) > oend))
return 0;
if (length >= (int)RUN_MASK) {
*token = (RUN_MASK << ML_BITS);
len = length - RUN_MASK;
for (; len > 254 ; len -= 255)
*op++ = 255;
*op++ = (u8)len;
} else
*token = (length << ML_BITS);
/* Copy Literals */
LZ4_BLINDCOPY(anchor, op, length);
_next_match:
/* Encode Offset */
LZ4_WRITE_LITTLEENDIAN_16(op, (u16)(ip - ref));
/* Start Counting */
ip += MINMATCH;
/* MinMatch verified */
ref += MINMATCH;
anchor = ip;
while (ip < MATCHLIMIT - (STEPSIZE - 1)) {
#if LZ4_ARCH64
u64 diff = A64(ref) ^ A64(ip);
#else
u32 diff = A32(ref) ^ A32(ip);
#endif
if (!diff) {
ip += STEPSIZE;
ref += STEPSIZE;
continue;
}
ip += LZ4_NBCOMMONBYTES(diff);
goto _endcount;
}
#if LZ4_ARCH64
if ((ip < (MATCHLIMIT - 3)) && (A32(ref) == A32(ip))) {
ip += 4;
ref += 4;
}
#endif
if ((ip < (MATCHLIMIT - 1)) && (A16(ref) == A16(ip))) {
ip += 2;
ref += 2;
}
if ((ip < MATCHLIMIT) && (*ref == *ip))
ip++;
_endcount:
/* Encode MatchLength */
len = (int)(ip - anchor);
/* Check output limit */
if (unlikely(op + (1 + LASTLITERALS) + (len >> 8) > oend))
return 0;
if (len >= (int)ML_MASK) {
*token += ML_MASK;
len -= ML_MASK;
for (; len > 509 ; len -= 510) {
*op++ = 255;
*op++ = 255;
}
if (len > 254) {
len -= 255;
*op++ = 255;
}
*op++ = (u8)len;
} else
*token += len;
/* Test end of chunk */
if (ip > mflimit) {
anchor = ip;
break;
}
/* Fill table */
hashtable[LZ4_HASH64K_VALUE(ip-2)] = (u16)(ip - 2 - base);
/* Test next position */
ref = base + hashtable[LZ4_HASH64K_VALUE(ip)];
hashtable[LZ4_HASH64K_VALUE(ip)] = (u16)(ip - base);
if (A32(ref) == A32(ip)) {
token = op++;
*token = 0;
goto _next_match;
}
/* Prepare next loop */
anchor = ip++;
forwardh = LZ4_HASH64K_VALUE(ip);
}
_last_literals:
/* Encode Last Literals */
lastrun = (int)(iend - anchor);
if (op + lastrun + 1 + (lastrun - RUN_MASK + 255) / 255 > oend)
return 0;
if (lastrun >= (int)RUN_MASK) {
*op++ = (RUN_MASK << ML_BITS);
lastrun -= RUN_MASK;
for (; lastrun > 254 ; lastrun -= 255)
*op++ = 255;
*op++ = (u8)lastrun;
} else
*op++ = (lastrun << ML_BITS);
memcpy(op, anchor, iend - anchor);
op += iend - anchor;
/* End */
return (int)(((char *)op) - dest);
}
int lz4_compress(const unsigned char *src, size_t src_len,
unsigned char *dst, size_t *dst_len, void *wrkmem)
{
int ret = -1;
int out_len = 0;
if (src_len < LZ4_64KLIMIT)
out_len = lz4_compress64kctx(wrkmem, src, dst, src_len,
lz4_compressbound(src_len));
else
out_len = lz4_compressctx(wrkmem, src, dst, src_len,
lz4_compressbound(src_len));
if (out_len < 0)
goto exit;
*dst_len = out_len;
return 0;
exit:
return ret;
}
EXPORT_SYMBOL(lz4_compress);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("LZ4 compressor");

337
lib/lz4/lz4_decompress.c Normal file
View File

@ -0,0 +1,337 @@
/*
* LZ4 Decompressor for Linux kernel
*
* Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
*
* Based on LZ4 implementation by Yann Collet.
*
* LZ4 - Fast LZ compression algorithm
* Copyright (C) 2011-2012, Yann Collet.
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at :
* - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
* - LZ4 source repository : http://code.google.com/p/lz4/
*/
#ifndef STATIC
#include <linux/module.h>
#include <linux/kernel.h>
#endif
#include <linux/lz4.h>
#include <asm/unaligned.h>
#include "lz4defs.h"
static int lz4_uncompress(const char *source, char *dest, int osize)
{
const BYTE *ip = (const BYTE *) source;
const BYTE *ref;
BYTE *op = (BYTE *) dest;
BYTE * const oend = op + osize;
BYTE *cpy;
unsigned token;
size_t length;
size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0};
#if LZ4_ARCH64
size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
#endif
while (1) {
/* get runlength */
token = *ip++;
length = (token >> ML_BITS);
if (length == RUN_MASK) {
size_t len;
len = *ip++;
for (; len == 255; length += 255)
len = *ip++;
if (unlikely(length > (size_t)(length + len)))
goto _output_error;
length += len;
}
/* copy literals */
cpy = op + length;
if (unlikely(cpy > oend - COPYLENGTH)) {
/*
* Error: not enough place for another match
* (min 4) + 5 literals
*/
if (cpy != oend)
goto _output_error;
memcpy(op, ip, length);
ip += length;
break; /* EOF */
}
LZ4_WILDCOPY(ip, op, cpy);
ip -= (op - cpy);
op = cpy;
/* get offset */
LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
ip += 2;
/* Error: offset create reference outside destination buffer */
if (unlikely(ref < (BYTE *const) dest))
goto _output_error;
/* get matchlength */
length = token & ML_MASK;
if (length == ML_MASK) {
for (; *ip == 255; length += 255)
ip++;
if (unlikely(length > (size_t)(length + *ip)))
goto _output_error;
length += *ip++;
}
/* copy repeated sequence */
if (unlikely((op - ref) < STEPSIZE)) {
#if LZ4_ARCH64
size_t dec64 = dec64table[op - ref];
#else
const int dec64 = 0;
#endif
op[0] = ref[0];
op[1] = ref[1];
op[2] = ref[2];
op[3] = ref[3];
op += 4;
ref += 4;
ref -= dec32table[op-ref];
PUT4(ref, op);
op += STEPSIZE - 4;
ref -= dec64;
} else {
LZ4_COPYSTEP(ref, op);
}
cpy = op + length - (STEPSIZE - 4);
if (cpy > (oend - COPYLENGTH)) {
/* Error: request to write beyond destination buffer */
if (cpy > oend)
goto _output_error;
if ((ref + COPYLENGTH) > oend ||
(op + COPYLENGTH) > oend)
goto _output_error;
LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
while (op < cpy)
*op++ = *ref++;
op = cpy;
/*
* Check EOF (should never happen, since last 5 bytes
* are supposed to be literals)
*/
if (op == oend)
goto _output_error;
continue;
}
LZ4_SECURECOPY(ref, op, cpy);
op = cpy; /* correction */
}
/* end of decoding */
return (int) (((char *)ip) - source);
/* write overflow error detected */
_output_error:
return -1;
}
static int lz4_uncompress_unknownoutputsize(const char *source, char *dest,
int isize, size_t maxoutputsize)
{
const BYTE *ip = (const BYTE *) source;
const BYTE *const iend = ip + isize;
const BYTE *ref;
BYTE *op = (BYTE *) dest;
BYTE * const oend = op + maxoutputsize;
BYTE *cpy;
size_t dec32table[] = {0, 3, 2, 3, 0, 0, 0, 0};
#if LZ4_ARCH64
size_t dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3};
#endif
/* Main Loop */
while (ip < iend) {
unsigned token;
size_t length;
/* get runlength */
token = *ip++;
length = (token >> ML_BITS);
if (length == RUN_MASK) {
int s = 255;
while ((ip < iend) && (s == 255)) {
s = *ip++;
if (unlikely(length > (size_t)(length + s)))
goto _output_error;
length += s;
}
}
/* copy literals */
cpy = op + length;
if ((cpy > oend - COPYLENGTH) ||
(ip + length > iend - COPYLENGTH)) {
if (cpy > oend)
goto _output_error;/* writes beyond buffer */
if (ip + length != iend)
goto _output_error;/*
* Error: LZ4 format requires
* to consume all input
* at this stage
*/
memcpy(op, ip, length);
op += length;
break;/* Necessarily EOF, due to parsing restrictions */
}
LZ4_WILDCOPY(ip, op, cpy);
ip -= (op - cpy);
op = cpy;
/* get offset */
LZ4_READ_LITTLEENDIAN_16(ref, cpy, ip);
ip += 2;
if (ref < (BYTE * const) dest)
goto _output_error;
/*
* Error : offset creates reference
* outside of destination buffer
*/
/* get matchlength */
length = (token & ML_MASK);
if (length == ML_MASK) {
while (ip < iend) {
int s = *ip++;
if (unlikely(length > (size_t)(length + s)))
goto _output_error;
length += s;
if (s == 255)
continue;
break;
}
}
/* copy repeated sequence */
if (unlikely((op - ref) < STEPSIZE)) {
#if LZ4_ARCH64
size_t dec64 = dec64table[op - ref];
#else
const int dec64 = 0;
#endif
op[0] = ref[0];
op[1] = ref[1];
op[2] = ref[2];
op[3] = ref[3];
op += 4;
ref += 4;
ref -= dec32table[op - ref];
PUT4(ref, op);
op += STEPSIZE - 4;
ref -= dec64;
} else {
LZ4_COPYSTEP(ref, op);
}
cpy = op + length - (STEPSIZE-4);
if (cpy > oend - COPYLENGTH) {
if (cpy > oend)
goto _output_error; /* write outside of buf */
LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
while (op < cpy)
*op++ = *ref++;
op = cpy;
/*
* Check EOF (should never happen, since last 5 bytes
* are supposed to be literals)
*/
if (op == oend)
goto _output_error;
continue;
}
LZ4_SECURECOPY(ref, op, cpy);
op = cpy; /* correction */
}
/* end of decoding */
return (int) (((char *) op) - dest);
/* write overflow error detected */
_output_error:
return -1;
}
int lz4_decompress(const unsigned char *src, size_t *src_len,
unsigned char *dest, size_t actual_dest_len)
{
int ret = -1;
int input_len = 0;
input_len = lz4_uncompress(src, dest, actual_dest_len);
if (input_len < 0)
goto exit_0;
*src_len = input_len;
return 0;
exit_0:
return ret;
}
#ifndef STATIC
EXPORT_SYMBOL(lz4_decompress);
#endif
int lz4_decompress_unknownoutputsize(const unsigned char *src, size_t src_len,
unsigned char *dest, size_t *dest_len)
{
int ret = -1;
int out_len = 0;
out_len = lz4_uncompress_unknownoutputsize(src, dest, src_len,
*dest_len);
if (out_len < 0)
goto exit_0;
*dest_len = out_len;
return 0;
exit_0:
return ret;
}
#ifndef STATIC
EXPORT_SYMBOL(lz4_decompress_unknownoutputsize);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("LZ4 Decompressor");
#endif

156
lib/lz4/lz4defs.h Normal file
View File

@ -0,0 +1,156 @@
/*
* lz4defs.h -- architecture specific defines
*
* 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.
*/
/*
* Detects 64 bits mode
*/
#if (defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) \
|| defined(__ppc64__) || defined(__LP64__))
#define LZ4_ARCH64 1
#else
#define LZ4_ARCH64 0
#endif
/*
* Architecture-specific macros
*/
#define BYTE u8
typedef struct _U16_S { u16 v; } U16_S;
typedef struct _U32_S { u32 v; } U32_S;
typedef struct _U64_S { u64 v; } U64_S;
#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) \
|| defined(CONFIG_ARM) && __LINUX_ARM_ARCH__ >= 6 \
&& defined(ARM_EFFICIENT_UNALIGNED_ACCESS)
#define A16(x) (((U16_S *)(x))->v)
#define A32(x) (((U32_S *)(x))->v)
#define A64(x) (((U64_S *)(x))->v)
#define PUT4(s, d) (A32(d) = A32(s))
#define PUT8(s, d) (A64(d) = A64(s))
#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \
do { \
A16(p) = v; \
p += 2; \
} while (0)
#else /* CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS */
#define A64(x) get_unaligned((u64 *)&(((U16_S *)(x))->v))
#define A32(x) get_unaligned((u32 *)&(((U16_S *)(x))->v))
#define A16(x) get_unaligned((u16 *)&(((U16_S *)(x))->v))
#define PUT4(s, d) \
put_unaligned(get_unaligned((const u32 *) s), (u32 *) d)
#define PUT8(s, d) \
put_unaligned(get_unaligned((const u64 *) s), (u64 *) d)
#define LZ4_WRITE_LITTLEENDIAN_16(p, v) \
do { \
put_unaligned(v, (u16 *)(p)); \
p += 2; \
} while (0)
#endif
#define COPYLENGTH 8
#define ML_BITS 4
#define ML_MASK ((1U << ML_BITS) - 1)
#define RUN_BITS (8 - ML_BITS)
#define RUN_MASK ((1U << RUN_BITS) - 1)
#define MEMORY_USAGE 14
#define MINMATCH 4
#define SKIPSTRENGTH 6
#define LASTLITERALS 5
#define MFLIMIT (COPYLENGTH + MINMATCH)
#define MINLENGTH (MFLIMIT + 1)
#define MAXD_LOG 16
#define MAXD (1 << MAXD_LOG)
#define MAXD_MASK (u32)(MAXD - 1)
#define MAX_DISTANCE (MAXD - 1)
#define HASH_LOG (MAXD_LOG - 1)
#define HASHTABLESIZE (1 << HASH_LOG)
#define MAX_NB_ATTEMPTS 256
#define OPTIMAL_ML (int)((ML_MASK-1)+MINMATCH)
#define LZ4_64KLIMIT ((1<<16) + (MFLIMIT - 1))
#define HASHLOG64K ((MEMORY_USAGE - 2) + 1)
#define HASH64KTABLESIZE (1U << HASHLOG64K)
#define LZ4_HASH_VALUE(p) (((A32(p)) * 2654435761U) >> \
((MINMATCH * 8) - (MEMORY_USAGE-2)))
#define LZ4_HASH64K_VALUE(p) (((A32(p)) * 2654435761U) >> \
((MINMATCH * 8) - HASHLOG64K))
#define HASH_VALUE(p) (((A32(p)) * 2654435761U) >> \
((MINMATCH * 8) - HASH_LOG))
#if LZ4_ARCH64/* 64-bit */
#define STEPSIZE 8
#define LZ4_COPYSTEP(s, d) \
do { \
PUT8(s, d); \
d += 8; \
s += 8; \
} while (0)
#define LZ4_COPYPACKET(s, d) LZ4_COPYSTEP(s, d)
#define LZ4_SECURECOPY(s, d, e) \
do { \
if (d < e) { \
LZ4_WILDCOPY(s, d, e); \
} \
} while (0)
#define HTYPE u32
#ifdef __BIG_ENDIAN
#define LZ4_NBCOMMONBYTES(val) (__builtin_clzll(val) >> 3)
#else
#define LZ4_NBCOMMONBYTES(val) (__builtin_ctzll(val) >> 3)
#endif
#else /* 32-bit */
#define STEPSIZE 4
#define LZ4_COPYSTEP(s, d) \
do { \
PUT4(s, d); \
d += 4; \
s += 4; \
} while (0)
#define LZ4_COPYPACKET(s, d) \
do { \
LZ4_COPYSTEP(s, d); \
LZ4_COPYSTEP(s, d); \
} while (0)
#define LZ4_SECURECOPY LZ4_WILDCOPY
#define HTYPE const u8*
#ifdef __BIG_ENDIAN
#define LZ4_NBCOMMONBYTES(val) (__builtin_clz(val) >> 3)
#else
#define LZ4_NBCOMMONBYTES(val) (__builtin_ctz(val) >> 3)
#endif
#endif
#define LZ4_READ_LITTLEENDIAN_16(d, s, p) \
(d = s - get_unaligned_le16(p))
#define LZ4_WILDCOPY(s, d, e) \
do { \
LZ4_COPYPACKET(s, d); \
} while (d < e)
#define LZ4_BLINDCOPY(s, d, l) \
do { \
u8 *e = (d) + l; \
LZ4_WILDCOPY(s, d, e); \
d = e; \
} while (0)

539
lib/lz4/lz4hc_compress.c Normal file
View File

@ -0,0 +1,539 @@
/*
* LZ4 HC - High Compression Mode of LZ4
* Copyright (C) 2011-2012, Yann Collet.
* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You can contact the author at :
* - LZ4 homepage : http://fastcompression.blogspot.com/p/lz4.html
* - LZ4 source repository : http://code.google.com/p/lz4/
*
* Changed for kernel use by:
* Chanho Min <chanho.min@lge.com>
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/lz4.h>
#include <asm/unaligned.h>
#include "lz4defs.h"
struct lz4hc_data {
const u8 *base;
HTYPE hashtable[HASHTABLESIZE];
u16 chaintable[MAXD];
const u8 *nexttoupdate;
} __attribute__((__packed__));
static inline int lz4hc_init(struct lz4hc_data *hc4, const u8 *base)
{
memset((void *)hc4->hashtable, 0, sizeof(hc4->hashtable));
memset(hc4->chaintable, 0xFF, sizeof(hc4->chaintable));
#if LZ4_ARCH64
hc4->nexttoupdate = base + 1;
#else
hc4->nexttoupdate = base;
#endif
hc4->base = base;
return 1;
}
/* Update chains up to ip (excluded) */
static inline void lz4hc_insert(struct lz4hc_data *hc4, const u8 *ip)
{
u16 *chaintable = hc4->chaintable;
HTYPE *hashtable = hc4->hashtable;
#if LZ4_ARCH64
const BYTE * const base = hc4->base;
#else
const int base = 0;
#endif
while (hc4->nexttoupdate < ip) {
const u8 *p = hc4->nexttoupdate;
size_t delta = p - (hashtable[HASH_VALUE(p)] + base);
if (delta > MAX_DISTANCE)
delta = MAX_DISTANCE;
chaintable[(size_t)(p) & MAXD_MASK] = (u16)delta;
hashtable[HASH_VALUE(p)] = (p) - base;
hc4->nexttoupdate++;
}
}
static inline size_t lz4hc_commonlength(const u8 *p1, const u8 *p2,
const u8 *const matchlimit)
{
const u8 *p1t = p1;
while (p1t < matchlimit - (STEPSIZE - 1)) {
#if LZ4_ARCH64
u64 diff = A64(p2) ^ A64(p1t);
#else
u32 diff = A32(p2) ^ A32(p1t);
#endif
if (!diff) {
p1t += STEPSIZE;
p2 += STEPSIZE;
continue;
}
p1t += LZ4_NBCOMMONBYTES(diff);
return p1t - p1;
}
#if LZ4_ARCH64
if ((p1t < (matchlimit-3)) && (A32(p2) == A32(p1t))) {
p1t += 4;
p2 += 4;
}
#endif
if ((p1t < (matchlimit - 1)) && (A16(p2) == A16(p1t))) {
p1t += 2;
p2 += 2;
}
if ((p1t < matchlimit) && (*p2 == *p1t))
p1t++;
return p1t - p1;
}
static inline int lz4hc_insertandfindbestmatch(struct lz4hc_data *hc4,
const u8 *ip, const u8 *const matchlimit, const u8 **matchpos)
{
u16 *const chaintable = hc4->chaintable;
HTYPE *const hashtable = hc4->hashtable;
const u8 *ref;
#if LZ4_ARCH64
const BYTE * const base = hc4->base;
#else
const int base = 0;
#endif
int nbattempts = MAX_NB_ATTEMPTS;
size_t repl = 0, ml = 0;
u16 delta;
/* HC4 match finder */
lz4hc_insert(hc4, ip);
ref = hashtable[HASH_VALUE(ip)] + base;
/* potential repetition */
if (ref >= ip-4) {
/* confirmed */
if (A32(ref) == A32(ip)) {
delta = (u16)(ip-ref);
repl = ml = lz4hc_commonlength(ip + MINMATCH,
ref + MINMATCH, matchlimit) + MINMATCH;
*matchpos = ref;
}
ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
}
while ((ref >= ip - MAX_DISTANCE) && nbattempts) {
nbattempts--;
if (*(ref + ml) == *(ip + ml)) {
if (A32(ref) == A32(ip)) {
size_t mlt =
lz4hc_commonlength(ip + MINMATCH,
ref + MINMATCH, matchlimit) + MINMATCH;
if (mlt > ml) {
ml = mlt;
*matchpos = ref;
}
}
}
ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
}
/* Complete table */
if (repl) {
const BYTE *ptr = ip;
const BYTE *end;
end = ip + repl - (MINMATCH-1);
/* Pre-Load */
while (ptr < end - delta) {
chaintable[(size_t)(ptr) & MAXD_MASK] = delta;
ptr++;
}
do {
chaintable[(size_t)(ptr) & MAXD_MASK] = delta;
/* Head of chain */
hashtable[HASH_VALUE(ptr)] = (ptr) - base;
ptr++;
} while (ptr < end);
hc4->nexttoupdate = end;
}
return (int)ml;
}
static inline int lz4hc_insertandgetwidermatch(struct lz4hc_data *hc4,
const u8 *ip, const u8 *startlimit, const u8 *matchlimit, int longest,
const u8 **matchpos, const u8 **startpos)
{
u16 *const chaintable = hc4->chaintable;
HTYPE *const hashtable = hc4->hashtable;
#if LZ4_ARCH64
const BYTE * const base = hc4->base;
#else
const int base = 0;
#endif
const u8 *ref;
int nbattempts = MAX_NB_ATTEMPTS;
int delta = (int)(ip - startlimit);
/* First Match */
lz4hc_insert(hc4, ip);
ref = hashtable[HASH_VALUE(ip)] + base;
while ((ref >= ip - MAX_DISTANCE) && (ref >= hc4->base)
&& (nbattempts)) {
nbattempts--;
if (*(startlimit + longest) == *(ref - delta + longest)) {
if (A32(ref) == A32(ip)) {
const u8 *reft = ref + MINMATCH;
const u8 *ipt = ip + MINMATCH;
const u8 *startt = ip;
while (ipt < matchlimit-(STEPSIZE - 1)) {
#if LZ4_ARCH64
u64 diff = A64(reft) ^ A64(ipt);
#else
u32 diff = A32(reft) ^ A32(ipt);
#endif
if (!diff) {
ipt += STEPSIZE;
reft += STEPSIZE;
continue;
}
ipt += LZ4_NBCOMMONBYTES(diff);
goto _endcount;
}
#if LZ4_ARCH64
if ((ipt < (matchlimit - 3))
&& (A32(reft) == A32(ipt))) {
ipt += 4;
reft += 4;
}
ipt += 2;
#endif
if ((ipt < (matchlimit - 1))
&& (A16(reft) == A16(ipt))) {
reft += 2;
}
if ((ipt < matchlimit) && (*reft == *ipt))
ipt++;
_endcount:
reft = ref;
while ((startt > startlimit)
&& (reft > hc4->base)
&& (startt[-1] == reft[-1])) {
startt--;
reft--;
}
if ((ipt - startt) > longest) {
longest = (int)(ipt - startt);
*matchpos = reft;
*startpos = startt;
}
}
}
ref -= (size_t)chaintable[(size_t)(ref) & MAXD_MASK];
}
return longest;
}
static inline int lz4_encodesequence(const u8 **ip, u8 **op, const u8 **anchor,
int ml, const u8 *ref)
{
int length, len;
u8 *token;
/* Encode Literal length */
length = (int)(*ip - *anchor);
token = (*op)++;
if (length >= (int)RUN_MASK) {
*token = (RUN_MASK << ML_BITS);
len = length - RUN_MASK;
for (; len > 254 ; len -= 255)
*(*op)++ = 255;
*(*op)++ = (u8)len;
} else
*token = (length << ML_BITS);
/* Copy Literals */
LZ4_BLINDCOPY(*anchor, *op, length);
/* Encode Offset */
LZ4_WRITE_LITTLEENDIAN_16(*op, (u16)(*ip - ref));
/* Encode MatchLength */
len = (int)(ml - MINMATCH);
if (len >= (int)ML_MASK) {
*token += ML_MASK;
len -= ML_MASK;
for (; len > 509 ; len -= 510) {
*(*op)++ = 255;
*(*op)++ = 255;
}
if (len > 254) {
len -= 255;
*(*op)++ = 255;
}
*(*op)++ = (u8)len;
} else
*token += len;
/* Prepare next loop */
*ip += ml;
*anchor = *ip;
return 0;
}
static int lz4_compresshcctx(struct lz4hc_data *ctx,
const char *source,
char *dest,
int isize)
{
const u8 *ip = (const u8 *)source;
const u8 *anchor = ip;
const u8 *const iend = ip + isize;
const u8 *const mflimit = iend - MFLIMIT;
const u8 *const matchlimit = (iend - LASTLITERALS);
u8 *op = (u8 *)dest;
int ml, ml2, ml3, ml0;
const u8 *ref = NULL;
const u8 *start2 = NULL;
const u8 *ref2 = NULL;
const u8 *start3 = NULL;
const u8 *ref3 = NULL;
const u8 *start0;
const u8 *ref0;
int lastrun;
ip++;
/* Main Loop */
while (ip < mflimit) {
ml = lz4hc_insertandfindbestmatch(ctx, ip, matchlimit, (&ref));
if (!ml) {
ip++;
continue;
}
/* saved, in case we would skip too much */
start0 = ip;
ref0 = ref;
ml0 = ml;
_search2:
if (ip+ml < mflimit)
ml2 = lz4hc_insertandgetwidermatch(ctx, ip + ml - 2,
ip + 1, matchlimit, ml, &ref2, &start2);
else
ml2 = ml;
/* No better match */
if (ml2 == ml) {
lz4_encodesequence(&ip, &op, &anchor, ml, ref);
continue;
}
if (start0 < ip) {
/* empirical */
if (start2 < ip + ml0) {
ip = start0;
ref = ref0;
ml = ml0;
}
}
/*
* Here, start0==ip
* First Match too small : removed
*/
if ((start2 - ip) < 3) {
ml = ml2;
ip = start2;
ref = ref2;
goto _search2;
}
_search3:
/*
* Currently we have :
* ml2 > ml1, and
* ip1+3 <= ip2 (usually < ip1+ml1)
*/
if ((start2 - ip) < OPTIMAL_ML) {
int correction;
int new_ml = ml;
if (new_ml > OPTIMAL_ML)
new_ml = OPTIMAL_ML;
if (ip + new_ml > start2 + ml2 - MINMATCH)
new_ml = (int)(start2 - ip) + ml2 - MINMATCH;
correction = new_ml - (int)(start2 - ip);
if (correction > 0) {
start2 += correction;
ref2 += correction;
ml2 -= correction;
}
}
/*
* Now, we have start2 = ip+new_ml,
* with new_ml=min(ml, OPTIMAL_ML=18)
*/
if (start2 + ml2 < mflimit)
ml3 = lz4hc_insertandgetwidermatch(ctx,
start2 + ml2 - 3, start2, matchlimit,
ml2, &ref3, &start3);
else
ml3 = ml2;
/* No better match : 2 sequences to encode */
if (ml3 == ml2) {
/* ip & ref are known; Now for ml */
if (start2 < ip+ml)
ml = (int)(start2 - ip);
/* Now, encode 2 sequences */
lz4_encodesequence(&ip, &op, &anchor, ml, ref);
ip = start2;
lz4_encodesequence(&ip, &op, &anchor, ml2, ref2);
continue;
}
/* Not enough space for match 2 : remove it */
if (start3 < ip + ml + 3) {
/*
* can write Seq1 immediately ==> Seq2 is removed,
* so Seq3 becomes Seq1
*/
if (start3 >= (ip + ml)) {
if (start2 < ip + ml) {
int correction =
(int)(ip + ml - start2);
start2 += correction;
ref2 += correction;
ml2 -= correction;
if (ml2 < MINMATCH) {
start2 = start3;
ref2 = ref3;
ml2 = ml3;
}
}
lz4_encodesequence(&ip, &op, &anchor, ml, ref);
ip = start3;
ref = ref3;
ml = ml3;
start0 = start2;
ref0 = ref2;
ml0 = ml2;
goto _search2;
}
start2 = start3;
ref2 = ref3;
ml2 = ml3;
goto _search3;
}
/*
* OK, now we have 3 ascending matches; let's write at least
* the first one ip & ref are known; Now for ml
*/
if (start2 < ip + ml) {
if ((start2 - ip) < (int)ML_MASK) {
int correction;
if (ml > OPTIMAL_ML)
ml = OPTIMAL_ML;
if (ip + ml > start2 + ml2 - MINMATCH)
ml = (int)(start2 - ip) + ml2
- MINMATCH;
correction = ml - (int)(start2 - ip);
if (correction > 0) {
start2 += correction;
ref2 += correction;
ml2 -= correction;
}
} else
ml = (int)(start2 - ip);
}
lz4_encodesequence(&ip, &op, &anchor, ml, ref);
ip = start2;
ref = ref2;
ml = ml2;
start2 = start3;
ref2 = ref3;
ml2 = ml3;
goto _search3;
}
/* Encode Last Literals */
lastrun = (int)(iend - anchor);
if (lastrun >= (int)RUN_MASK) {
*op++ = (RUN_MASK << ML_BITS);
lastrun -= RUN_MASK;
for (; lastrun > 254 ; lastrun -= 255)
*op++ = 255;
*op++ = (u8) lastrun;
} else
*op++ = (lastrun << ML_BITS);
memcpy(op, anchor, iend - anchor);
op += iend - anchor;
/* End */
return (int) (((char *)op) - dest);
}
int lz4hc_compress(const unsigned char *src, size_t src_len,
unsigned char *dst, size_t *dst_len, void *wrkmem)
{
int ret = -1;
int out_len = 0;
struct lz4hc_data *hc4 = (struct lz4hc_data *)wrkmem;
lz4hc_init(hc4, (const u8 *)src);
out_len = lz4_compresshcctx((struct lz4hc_data *)hc4, (const u8 *)src,
(char *)dst, (int)src_len);
if (out_len < 0)
goto exit;
*dst_len = out_len;
return 0;
exit:
return ret;
}
EXPORT_SYMBOL(lz4hc_compress);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("LZ4HC compressor");

View File

@ -310,6 +310,11 @@ cmd_lzo = (cat $(filter-out FORCE,$^) | \
lzop -9 && $(call size_append, $(filter-out FORCE,$^))) > $@ || \
(rm -f $@ ; false)
quiet_cmd_lz4 = LZ4 $@
cmd_lz4 = (cat $(filter-out FORCE,$^) | \
lz4c -l -c1 stdin stdout && $(call size_append, $(filter-out FORCE,$^))) > $@ || \
(rm -f $@ ; false)
# U-Boot mkimage
# ---------------------------------------------------------------------------

View File

@ -90,6 +90,15 @@ config RD_LZO
Support loading of a LZO encoded initial ramdisk or cpio buffer
If unsure, say N.
config RD_LZ4
bool "Support initial ramdisks compressed using LZ4" if EXPERT
default !EXPERT
depends on BLK_DEV_INITRD
select DECOMPRESS_LZ4
help
Support loading of a LZ4 encoded initial ramdisk or cpio buffer
If unsure, say N.
choice
prompt "Built-in initramfs compression mode" if INITRAMFS_SOURCE!=""
help