diff --git a/payloads/libpayload/Kconfig b/payloads/libpayload/Kconfig index dee970c778..5da4f92ac1 100644 --- a/payloads/libpayload/Kconfig +++ b/payloads/libpayload/Kconfig @@ -172,6 +172,13 @@ config LZMA help LZMA decoder implementation, usable eg. by CBFS, but also externally. + +config LZ4 + bool "LZ4 decoder" + default y + help + Decoder implementation for the LZ4 compression algorithm. + Adds standalone functions (CBFS support coming soon). endmenu menu "Console Options" diff --git a/payloads/libpayload/LICENSES b/payloads/libpayload/LICENSES index f340eadb7d..ab8daa46df 100644 --- a/payloads/libpayload/LICENSES +++ b/payloads/libpayload/LICENSES @@ -131,3 +131,7 @@ holders, and the exact license terms that apply. Source: GNU C Library (glibc), http://www.gnu.org/software/libc/libc.html Original files: sysdeps/i386/memset.c Current version we use: 2.14 + +* liblz4/lz4.c: 2-clause BSD + Source: LZ4 library, https://github.com/Cyan4973/lz4 + Current version we use: r130 (baf78e7e4dcbdf824a76f990ffeb573d113bbbdb) diff --git a/payloads/libpayload/Makefile.inc b/payloads/libpayload/Makefile.inc index c8a49fc281..b84e322941 100644 --- a/payloads/libpayload/Makefile.inc +++ b/payloads/libpayload/Makefile.inc @@ -45,6 +45,7 @@ classes-$(CONFIG_LP_CURSES) += libcurses classes-$(CONFIG_LP_PDCURSES) += libmenu libform libpanel classes-$(CONFIG_LP_CBFS) += libcbfs classes-$(CONFIG_LP_LZMA) += liblzma +classes-$(CONFIG_LP_LZ4) += liblz4 classes-$(CONFIG_LP_REMOTEGDB) += libgdb libraries := $(classes-y) classes-y += head.o @@ -54,6 +55,7 @@ subdirs-y += crypto libc drivers libpci gdb subdirs-$(CONFIG_LP_CURSES) += curses subdirs-$(CONFIG_LP_CBFS) += libcbfs subdirs-$(CONFIG_LP_LZMA) += liblzma +subdirs-$(CONFIG_LP_LZ4) += liblz4 INCLUDES := -Iinclude -Iinclude/$(ARCHDIR-y) -I$(obj) -include include/kconfig.h diff --git a/payloads/libpayload/include/lz4.h b/payloads/libpayload/include/lz4.h new file mode 100644 index 0000000000..1f2830db46 --- /dev/null +++ b/payloads/libpayload/include/lz4.h @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + */ + +#ifndef __LZ4_H_ +#define __LZ4_H_ + +#include + +/* Decompresses an LZ4F image (multiple LZ4 blocks with frame header) from src + * to dst, ensuring that it doesn't read more than srcn bytes and doesn't write + * more than dstn. Buffer sizes must stay below 2GB. + * Returns amount of decompressed bytes, or 0 on error. + */ +size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn); + +/* Same as ulz4fn() but does not perform any bounds checks. */ +size_t ulz4f(const void *src, void *dst); + +#endif /* __LZO_H_ */ diff --git a/payloads/libpayload/liblz4/Makefile.inc b/payloads/libpayload/liblz4/Makefile.inc new file mode 100644 index 0000000000..272a5a5c46 --- /dev/null +++ b/payloads/libpayload/liblz4/Makefile.inc @@ -0,0 +1,28 @@ +## +## Copyright 2015 Google Inc. +## +## Redistribution and use in source and binary forms, with or without +## modification, are permitted provided that the following conditions +## are met: +## 1. Redistributions of source code must retain the above copyright +## notice, this list of conditions and the following disclaimer. +## 2. 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. +## 3. The name of the author may not be used to endorse or promote products +## derived from this software without specific prior written permission. +## +## THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +## + +liblz4-$(CONFIG_LP_LZ4) += lz4_wrapper.c diff --git a/payloads/libpayload/liblz4/lz4.c b/payloads/libpayload/liblz4/lz4.c new file mode 100644 index 0000000000..fb89090ee2 --- /dev/null +++ b/payloads/libpayload/liblz4/lz4.c @@ -0,0 +1,266 @@ +/* + LZ4 - Fast LZ compression algorithm + Copyright (C) 2011-2015, 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 source repository : https://github.com/Cyan4973/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ + + +/************************************** +* Reading and writing into memory +**************************************/ + +/* customized version of memcpy, which may overwrite up to 7 bytes beyond dstEnd */ +static void LZ4_wildCopy(void* dstPtr, const void* srcPtr, void* dstEnd) +{ + BYTE* d = (BYTE*)dstPtr; + const BYTE* s = (const BYTE*)srcPtr; + BYTE* e = (BYTE*)dstEnd; + do { LZ4_copy8(d,s); d+=8; s+=8; } while (d oend-MFLIMIT)) oexit = oend-MFLIMIT; /* targetOutputSize too high => decode everything */ + if ((endOnInput) && (unlikely(outputSize==0))) return ((inputSize==1) && (*ip==0)) ? 0 : -1; /* Empty output buffer */ + if ((!endOnInput) && (unlikely(outputSize==0))) return (*ip==0?1:-1); + + + /* Main Loop */ + while (1) + { + unsigned token; + size_t length; + const BYTE* match; + + /* get literal length */ + token = *ip++; + if ((length=(token>>ML_BITS)) == RUN_MASK) + { + unsigned s; + do + { + s = *ip++; + length += s; + } + while (likely((endOnInput)?ip(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) + || ((!endOnInput) && (cpy>oend-COPYLENGTH))) + { + if (partialDecoding) + { + if (cpy > oend) goto _output_error; /* Error : write attempt beyond end of output buffer */ + if ((endOnInput) && (ip+length > iend)) goto _output_error; /* Error : read attempt beyond end of input buffer */ + } + else + { + if ((!endOnInput) && (cpy != oend)) goto _output_error; /* Error : block decoding must stop exactly there */ + if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) goto _output_error; /* Error : input must be consumed */ + } + memcpy(op, ip, length); + ip += length; + op += length; + break; /* Necessarily EOF, due to parsing restrictions */ + } + LZ4_wildCopy(op, ip, cpy); + ip += length; op = cpy; + + /* get offset */ + match = cpy - LZ4_readLE16(ip); ip+=2; + if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside destination buffer */ + + /* get matchlength */ + length = token & ML_MASK; + if (length == ML_MASK) + { + unsigned s; + do + { + if ((endOnInput) && (ip > iend-LASTLITERALS)) goto _output_error; + s = *ip++; + length += s; + } while (s==255); + if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)op)) goto _output_error; /* overflow detection */ + } + length += MINMATCH; + + /* check external dictionary */ + if ((dict==usingExtDict) && (match < lowPrefix)) + { + if (unlikely(op+length > oend-LASTLITERALS)) goto _output_error; /* doesn't respect parsing restriction */ + + if (length <= (size_t)(lowPrefix-match)) + { + /* match can be copied as a single segment from external dictionary */ + match = dictEnd - (lowPrefix-match); + memmove(op, match, length); op += length; + } + else + { + /* match encompass external dictionary and current segment */ + size_t copySize = (size_t)(lowPrefix-match); + memcpy(op, dictEnd - copySize, copySize); + op += copySize; + copySize = length - copySize; + if (copySize > (size_t)(op-lowPrefix)) /* overlap within current segment */ + { + BYTE* const endOfMatch = op + copySize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) *op++ = *copyFrom++; + } + else + { + memcpy(op, lowPrefix, copySize); + op += copySize; + } + } + continue; + } + + /* copy repeated sequence */ + cpy = op + length; + if (unlikely((op-match)<8)) + { + const size_t dec64 = dec64table[op-match]; + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += dec32table[op-match]; + LZ4_copy4(op+4, match); + op += 8; match -= dec64; + } else { LZ4_copy8(op, match); op+=8; match+=8; } + + if (unlikely(cpy>oend-12)) + { + if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals */ + if (op < oend-8) + { + LZ4_wildCopy(op, match, oend-8); + match += (oend-8) - op; + op = oend-8; + } + while (op +#include +#include +#include + +/* LZ4 comes with its own supposedly portable memory access functions, but they + * seem to be very inefficient in practice (at least on ARM64). Since libpayload + * knows about endinaness and allows some basic assumptions (such as unaligned + * access support), we can easily write the ones we need ourselves. */ +static u16 LZ4_readLE16(const void *src) { return le16toh(*(u16 *)src); } +static void LZ4_copy4(void *dst, const void *src) { *(u32 *)dst = *(u32 *)src; } +static void LZ4_copy8(void *dst, const void *src) { *(u64 *)dst = *(u64 *)src; } + +typedef uint8_t BYTE; +typedef uint16_t U16; +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; + +#define FORCE_INLINE static inline __attribute__((always_inline)) +#define likely(expr) __builtin_expect((expr) != 0, 1) +#define unlikely(expr) __builtin_expect((expr) != 0, 0) + +/* Unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */ +#include "lz4.c" /* #include for inlining, do not link! */ + +#define LZ4F_MAGICNUMBER 0x184D2204 + +struct lz4_frame_header { + u32 magic; + union { + u8 flags; + struct { + u8 reserved0 : 2; + u8 has_content_checksum : 1; + u8 has_content_size : 1; + u8 has_block_checksum : 1; + u8 independent_blocks : 1; + u8 version : 2; + }; + }; + union { + u8 block_descriptor; + struct { + u8 reserved1 : 4; + u8 max_block_size : 3; + u8 reserved2 : 1; + }; + }; + /* + u64 content_size iff has_content_size is set */ + /* + u8 header_checksum */ +} __attribute__((packed)); + +struct lz4_block_header { + union { + u32 raw; + struct { + u32 size : 31; + u32 not_compressed : 1; + }; + }; + /* + size bytes of data */ + /* + u32 block_checksum iff has_block_checksum is set */ +} __attribute__((packed)); + +size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn) +{ + const void *in = src; + void *out = dst; + int has_block_checksum; + + { /* With in-place decompression the header may become invalid later. */ + const struct lz4_frame_header *h = in; + + if (srcn < sizeof(*h) + sizeof(u64) + sizeof(u8)) + return 0; /* input overrun */ + + /* We assume there's always only a single, standard frame. */ + if (le32toh(h->magic) != LZ4F_MAGICNUMBER || h->version != 1) + return 0; /* unknown format */ + if (h->reserved0 || h->reserved1 || h->reserved2) + return 0; /* reserved must be zero */ + if (!h->independent_blocks) + return 0; /* we don't support block dependency */ + has_block_checksum = h->has_block_checksum; + + in += sizeof(*h); + if (h->has_content_size) + in += sizeof(u64); + in += sizeof(u8); + } + + while (1) { + struct lz4_block_header b = { .raw = le32toh(*(u32 *)in) }; + in += sizeof(struct lz4_block_header); + + if (in - src + b.size > srcn) + return 0; /* input overrun */ + + if (!b.size) + return out - dst; /* decompression successful */ + + if (b.not_compressed) { + memcpy(out, in, b.size); + out += b.size; + } else { + /* constant folding essential, do not touch params! */ + int ret = LZ4_decompress_generic(in, out, b.size, + dst + dstn - out, endOnInputSize, + full, 0, noDict, out, NULL, 0); + if (ret < 0) + return 0; /* decompression error */ + else + out += ret; + } + + in += b.size; + if (has_block_checksum) + in += sizeof(u32); + } +} + +size_t ulz4f(const void *src, void *dst) +{ + /* LZ4 uses signed size parameters, so can't just use ((u32)-1) here. */ + return ulz4fn(src, 1*GiB, dst, 1*GiB); +}