diff options
Diffstat (limited to 'src/commonlib/bsd')
-rw-r--r-- | src/commonlib/bsd/include/commonlib/bsd/cb_err.h | 42 | ||||
-rw-r--r-- | src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h | 190 | ||||
-rw-r--r-- | src/commonlib/bsd/include/commonlib/bsd/compiler.h | 53 | ||||
-rw-r--r-- | src/commonlib/bsd/include/commonlib/bsd/compression.h | 21 | ||||
-rw-r--r-- | src/commonlib/bsd/include/commonlib/bsd/fmap_serialized.h | 41 | ||||
-rw-r--r-- | src/commonlib/bsd/include/commonlib/bsd/helpers.h | 89 | ||||
-rw-r--r-- | src/commonlib/bsd/lz4.c.inc | 280 | ||||
-rw-r--r-- | src/commonlib/bsd/lz4_wrapper.c | 174 |
8 files changed, 890 insertions, 0 deletions
diff --git a/src/commonlib/bsd/include/commonlib/bsd/cb_err.h b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h new file mode 100644 index 0000000000..ab419a7709 --- /dev/null +++ b/src/commonlib/bsd/include/commonlib/bsd/cb_err.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-or-later */ + +#ifndef _COMMONLIB_BSD_CB_ERR_H_ +#define _COMMONLIB_BSD_CB_ERR_H_ + +#include <stdint.h> + +/** + * coreboot error codes + * + * Common error definitions that can be used for any function. All error values + * should be negative -- when useful, positive values can also be used to denote + * success. Allocate a new group or errors every 100 values. + */ +enum cb_err { + CB_SUCCESS = 0, /**< Call completed successfully */ + CB_ERR = -1, /**< Generic error code */ + CB_ERR_ARG = -2, /**< Invalid argument */ + + /* NVRAM/CMOS errors */ + CB_CMOS_OTABLE_DISABLED = -100, /**< Option table disabled */ + CB_CMOS_LAYOUT_NOT_FOUND = -101, /**< Layout file not found */ + CB_CMOS_OPTION_NOT_FOUND = -102, /**< Option string not found */ + CB_CMOS_ACCESS_ERROR = -103, /**< CMOS access error */ + CB_CMOS_CHECKSUM_INVALID = -104, /**< CMOS checksum is invalid */ + + /* Keyboard test failures */ + CB_KBD_CONTROLLER_FAILURE = -200, + CB_KBD_INTERFACE_FAILURE = -201, + + /* I2C controller failures */ + CB_I2C_NO_DEVICE = -300, /**< Device is not responding */ + CB_I2C_BUSY = -301, /**< Device tells it's busy */ + CB_I2C_PROTOCOL_ERROR = -302, /**< Data lost or spurious slave + device response, try again? */ + CB_I2C_TIMEOUT = -303, /**< Transmission timed out */ +}; + +/* Don't typedef the enum directly, so the size is unambiguous for serialization. */ +typedef int32_t cb_err_t; + +#endif /* _COMMONLIB_BSD_CB_ERR_H_ */ diff --git a/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h b/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h new file mode 100644 index 0000000000..d2fc6267ad --- /dev/null +++ b/src/commonlib/bsd/include/commonlib/bsd/cbfs_serialized.h @@ -0,0 +1,190 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ + +#ifndef _CBFS_SERIALIZED_H_ +#define _CBFS_SERIALIZED_H_ + +#include <stdint.h> + +/** These are standard values for the known compression + algorithms that coreboot knows about for stages and + payloads. Of course, other CBFS users can use whatever + values they want, as long as they understand them. */ + +#define CBFS_COMPRESS_NONE 0 +#define CBFS_COMPRESS_LZMA 1 +#define CBFS_COMPRESS_LZ4 2 + +/** These are standard component types for well known + components (i.e - those that coreboot needs to consume. + Users are welcome to use any other value for their + components */ + +#define CBFS_TYPE_DELETED 0x00000000 +#define CBFS_TYPE_DELETED2 0xffffffff +#define CBFS_TYPE_STAGE 0x10 +#define CBFS_TYPE_SELF 0x20 +#define CBFS_TYPE_FIT 0x21 +#define CBFS_TYPE_OPTIONROM 0x30 +#define CBFS_TYPE_BOOTSPLASH 0x40 +#define CBFS_TYPE_RAW 0x50 +#define CBFS_TYPE_VSA 0x51 +#define CBFS_TYPE_MBI 0x52 +#define CBFS_TYPE_MICROCODE 0x53 +#define CBFS_TYPE_FSP 0x60 +#define CBFS_TYPE_MRC 0x61 +#define CBFS_TYPE_MMA 0x62 +#define CBFS_TYPE_EFI 0x63 +#define CBFS_TYPE_STRUCT 0x70 +#define CBFS_COMPONENT_CMOS_DEFAULT 0xaa +#define CBFS_TYPE_SPD 0xab +#define CBFS_TYPE_MRC_CACHE 0xac +#define CBFS_COMPONENT_CMOS_LAYOUT 0x01aa + +#define CBFS_HEADER_MAGIC 0x4F524243 +#define CBFS_HEADER_VERSION1 0x31313131 +#define CBFS_HEADER_VERSION2 0x31313132 +#define CBFS_HEADER_VERSION CBFS_HEADER_VERSION2 + +/* this is the master cbfs header - it must be located somewhere available + * to bootblock (to load romstage). The last 4 bytes in the image contain its + * relative offset from the end of the image (as a 32-bit signed integer). */ + +struct cbfs_header { + uint32_t magic; + uint32_t version; + uint32_t romsize; + uint32_t bootblocksize; + uint32_t align; /* fixed to 64 bytes */ + uint32_t offset; + uint32_t architecture; + uint32_t pad[1]; +} __packed; + +/* this used to be flexible, but wasn't ever set to something different. */ +#define CBFS_ALIGNMENT 64 + +/* "Unknown" refers to CBFS headers version 1, + * before the architecture was defined (i.e., x86 only). + */ +#define CBFS_ARCHITECTURE_UNKNOWN 0xFFFFFFFF +#define CBFS_ARCHITECTURE_X86 0x00000001 +#define CBFS_ARCHITECTURE_ARM 0x00000010 + +/** This is a component header - every entry in the CBFS + will have this header. + + This is how the component is arranged in the ROM: + + -------------- <- 0 + component header + -------------- <- sizeof(struct component) + component name + -------------- <- offset + data + ... + -------------- <- offset + len +*/ + +#define CBFS_FILE_MAGIC "LARCHIVE" + +struct cbfs_file { + char magic[8]; + uint32_t len; + uint32_t type; + uint32_t attributes_offset; + uint32_t offset; +} __packed; + +/* The common fields of extended cbfs file attributes. + Attributes are expected to start with tag/len, then append their + specific fields. */ +struct cbfs_file_attribute { + uint32_t tag; + /* len covers the whole structure, incl. tag and len */ + uint32_t len; + uint8_t data[0]; +} __packed; + +/* Depending on how the header was initialized, it may be backed with 0x00 or + * 0xff. Support both. */ +#define CBFS_FILE_ATTR_TAG_UNUSED 0 +#define CBFS_FILE_ATTR_TAG_UNUSED2 0xffffffff +#define CBFS_FILE_ATTR_TAG_COMPRESSION 0x42435a4c +#define CBFS_FILE_ATTR_TAG_HASH 0x68736148 +#define CBFS_FILE_ATTR_TAG_POSITION 0x42435350 /* PSCB */ +#define CBFS_FILE_ATTR_TAG_ALIGNMENT 0x42434c41 /* ALCB */ +#define CBFS_FILE_ATTR_TAG_IBB 0x32494242 /* Initial BootBlock */ + +struct cbfs_file_attr_compression { + uint32_t tag; + uint32_t len; + /* whole file compression format. 0 if no compression. */ + uint32_t compression; + uint32_t decompressed_size; +} __packed; + +struct cbfs_file_attr_hash { + uint32_t tag; + uint32_t len; + uint32_t hash_type; + /* hash_data is len - sizeof(struct) bytes */ + uint8_t hash_data[]; +} __packed; + +struct cbfs_file_attr_position { + uint32_t tag; + uint32_t len; + uint32_t position; +} __packed; + +struct cbfs_file_attr_align { + uint32_t tag; + uint32_t len; + uint32_t alignment; +} __packed; + + +/*** Component sub-headers ***/ + +/* Following are component sub-headers for the "standard" + component types */ + +/** This is the sub-header for stage components. Stages are + loaded by coreboot during the normal boot process */ + +struct cbfs_stage { + uint32_t compression; /** Compression type */ + uint64_t entry; /** entry point */ + uint64_t load; /** Where to load in memory */ + uint32_t len; /** length of data to load */ + uint32_t memlen; /** total length of object in memory */ +} __packed; + +/** this is the sub-header for payload components. Payloads + are loaded by coreboot at the end of the boot process */ + +struct cbfs_payload_segment { + uint32_t type; + uint32_t compression; + uint32_t offset; + uint64_t load_addr; + uint32_t len; + uint32_t mem_len; +} __packed; + +struct cbfs_payload { + struct cbfs_payload_segment segments; +}; + +#define PAYLOAD_SEGMENT_CODE 0x434F4445 +#define PAYLOAD_SEGMENT_DATA 0x44415441 +#define PAYLOAD_SEGMENT_BSS 0x42535320 +#define PAYLOAD_SEGMENT_PARAMS 0x50415241 +#define PAYLOAD_SEGMENT_ENTRY 0x454E5452 + +struct cbfs_optionrom { + uint32_t compression; + uint32_t len; +} __packed; + +#endif /* _CBFS_SERIALIZED_H_ */ diff --git a/src/commonlib/bsd/include/commonlib/bsd/compiler.h b/src/commonlib/bsd/include/commonlib/bsd/compiler.h new file mode 100644 index 0000000000..ee2ff88d10 --- /dev/null +++ b/src/commonlib/bsd/include/commonlib/bsd/compiler.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ + +#ifndef _COMMONLIB_BSD_COMPILER_H_ +#define _COMMONLIB_BSD_COMPILER_H_ + +#ifndef __packed +#if defined(__WIN32) || defined(__WIN64) +#define __packed __attribute__((gcc_struct, packed)) +#else +#define __packed __attribute__((packed)) +#endif +#endif + +#ifndef __aligned +#define __aligned(x) __attribute__((aligned(x))) +#endif + +#ifndef __always_unused +#define __always_unused __attribute__((unused)) +#endif + +#ifndef __must_check +#define __must_check __attribute__((warn_unused_result)) +#endif + +#ifndef __weak +#define __weak __attribute__((weak)) +#endif + +#ifndef __noreturn +#define __noreturn __attribute__((noreturn)) +#endif + +#ifndef __always_inline +#define __always_inline inline __attribute__((always_inline)) +#endif + +/* This evaluates to the type of the first expression, unless that is constant + in which case it evalutates to the type of the second. This is useful when + assigning macro parameters to temporary variables, because that would + normally circumvent the special loosened type promotion rules for integer + literals. By using this macro, the promotion can happen at the time the + literal is assigned to the temporary variable. If the literal doesn't fit in + the chosen type, -Werror=overflow will catch it, so this should be safe. */ +#define __TYPEOF_UNLESS_CONST(expr, fallback_expr) __typeof__( \ + __builtin_choose_expr(__builtin_constant_p(expr), fallback_expr, expr)) + +/* This creates a unique local variable name for use in macros. */ +#define __TMPNAME_3(i) __tmpname_##i +#define __TMPNAME_2(i) __TMPNAME_3(i) +#define __TMPNAME __TMPNAME_2(__COUNTER__) + +#endif diff --git a/src/commonlib/bsd/include/commonlib/bsd/compression.h b/src/commonlib/bsd/include/commonlib/bsd/compression.h new file mode 100644 index 0000000000..873e7e4e15 --- /dev/null +++ b/src/commonlib/bsd/include/commonlib/bsd/compression.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ + +#ifndef _COMMONLIB_COMPRESSION_H_ +#define _COMMONLIB_COMPRESSION_H_ + +#include <stddef.h> + +/* 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. Can decompress files loaded + * to the end of a buffer in-place, as long as buffer is larger than the final + * output size. (Usually just a few bytes, but may be up to (8 + dstn/255) in + * worst case. Will reliably return an error if buffer was too small.) + * 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 /* _COMMONLIB_COMPRESSION_H_ */ diff --git a/src/commonlib/bsd/include/commonlib/bsd/fmap_serialized.h b/src/commonlib/bsd/include/commonlib/bsd/fmap_serialized.h new file mode 100644 index 0000000000..3d328c45b8 --- /dev/null +++ b/src/commonlib/bsd/include/commonlib/bsd/fmap_serialized.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ + +#ifndef FLASHMAP_SERIALIZED_H__ +#define FLASHMAP_SERIALIZED_H__ + +#include <stdint.h> + +#define FMAP_SIGNATURE "__FMAP__" +#define FMAP_VER_MAJOR 1 /* this header's FMAP minor version */ +#define FMAP_VER_MINOR 1 /* this header's FMAP minor version */ +#define FMAP_STRLEN 32 /* maximum length for strings, */ + /* including null-terminator */ + +enum fmap_flags { + FMAP_AREA_STATIC = 1 << 0, + FMAP_AREA_COMPRESSED = 1 << 1, + FMAP_AREA_RO = 1 << 2, + FMAP_AREA_PRESERVE = 1 << 3, +}; + +/* Mapping of volatile and static regions in firmware binary */ +struct fmap_area { + uint32_t offset; /* offset relative to base */ + uint32_t size; /* size in bytes */ + uint8_t name[FMAP_STRLEN]; /* descriptive name */ + uint16_t flags; /* flags for this area */ +} __packed; + +struct fmap { + uint8_t signature[8]; /* "__FMAP__" (0x5F5F464D41505F5F) */ + uint8_t ver_major; /* major version */ + uint8_t ver_minor; /* minor version */ + uint64_t base; /* address of the firmware binary */ + uint32_t size; /* size of firmware binary in bytes */ + uint8_t name[FMAP_STRLEN]; /* name of this firmware binary */ + uint16_t nareas; /* number of areas described by + fmap_areas[] below */ + struct fmap_area areas[]; +} __packed; + +#endif /* FLASHMAP_SERIALIZED_H__ */ diff --git a/src/commonlib/bsd/include/commonlib/bsd/helpers.h b/src/commonlib/bsd/include/commonlib/bsd/helpers.h new file mode 100644 index 0000000000..a305df0cd5 --- /dev/null +++ b/src/commonlib/bsd/include/commonlib/bsd/helpers.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ + +#ifndef COMMONLIB_BSD_HELPERS_H +#define COMMONLIB_BSD_HELPERS_H + +#ifndef __ASSEMBLER__ +#include <commonlib/bsd/compiler.h> +#include <stddef.h> +#endif + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#endif + +#define ALIGN(x, a) __ALIGN_MASK(x, (__typeof__(x))(a)-1UL) +#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) +#define ALIGN_UP(x, a) ALIGN((x), (a)) +#define ALIGN_DOWN(x, a) ((x) & ~((__typeof__(x))(a)-1UL)) +#define IS_ALIGNED(x, a) (((x) & ((__typeof__(x))(a)-1UL)) == 0) + +/* Double-evaluation unsafe min/max, for bitfields and outside of functions */ +#define __CMP_UNSAFE(a, b, op) ((a) op (b) ? (a) : (b)) +#define MIN_UNSAFE(a, b) __CMP_UNSAFE(a, b, <) +#define MAX_UNSAFE(a, b) __CMP_UNSAFE(a, b, >) + +#define __CMP_SAFE(a, b, op, var_a, var_b) ({ \ + __TYPEOF_UNLESS_CONST(a, b) var_a = (a); \ + __TYPEOF_UNLESS_CONST(b, a) var_b = (b); \ + var_a op var_b ? var_a : var_b; \ +}) + +#define __CMP(a, b, op) __builtin_choose_expr( \ + __builtin_constant_p(a) && __builtin_constant_p(b), \ + __CMP_UNSAFE(a, b, op), __CMP_SAFE(a, b, op, __TMPNAME, __TMPNAME)) + +#ifndef MIN +#define MIN(a, b) __CMP(a, b, <) +#endif +#ifndef MAX +#define MAX(a, b) __CMP(a, b, >) +#endif + +#ifndef ABS +#define ABS(a) ({ \ + __typeof__(a) _abs_local_a = (a); \ + (_abs_local_a < 0) ? (-_abs_local_a) : _abs_local_a; \ +}) +#endif + +#define IS_POWER_OF_2(x) ({ \ + __typeof__(x) _power_local_x = (x); \ + (_power_local_x & (_power_local_x - 1)) == 0; \ +}) + +#define DIV_ROUND_UP(x, y) ({ \ + __typeof__(x) _div_local_x = (x); \ + __typeof__(y) _div_local_y = (y); \ + (_div_local_x + _div_local_y - 1) / _div_local_y; \ +}) + +#define SWAP(a, b) do { \ + __typeof__(&(a)) _swap_local_a = &(a); \ + __typeof__(&(b)) _swap_local_b = &(b); \ + __typeof__(a) _swap_local_tmp = *_swap_local_a; \ + *_swap_local_a = *_swap_local_b; \ + *_swap_local_b = _swap_local_tmp; \ +} while (0) + +/* Standard units. */ +#define KiB (1<<10) +#define MiB (1<<20) +#define GiB (1<<30) + +#define KHz (1000) +#define MHz (1000 * KHz) +#define GHz (1000 * MHz) + +#ifndef offsetof +#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) +#endif + +#define check_member(structure, member, offset) _Static_assert( \ + offsetof(struct structure, member) == offset, \ + "`struct " #structure "` offset for `" #member "` is not " #offset) + +/* Calculate size of structure member. */ +#define member_size(type, member) (sizeof(((type *)0)->member)) + +#endif /* COMMONLIB_BSD_HELPERS_H */ diff --git a/src/commonlib/bsd/lz4.c.inc b/src/commonlib/bsd/lz4.c.inc new file mode 100644 index 0000000000..b3be4e5b44 --- /dev/null +++ b/src/commonlib/bsd/lz4.c.inc @@ -0,0 +1,280 @@ +/* + 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 variant of memcpy, which can 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* const e = (BYTE*)dstEnd; + +#if 0 + const size_t l2 = 8 - (((size_t)d) & (sizeof(void*)-1)); + LZ4_copy8(d,s); if (d>e-9) return; + d+=l2; s+=l2; +#endif /* join to align */ + + do { LZ4_copy8(d,s); d+=8; s+=8; } while (d<e); +} + + +/************************************** +* Common Constants +**************************************/ +#define MINMATCH 4 + +#define WILDCOPYLENGTH 8 +#define LASTLITERALS 5 +#define MFLIMIT (WILDCOPYLENGTH+MINMATCH) +static const int LZ4_minLength = (MFLIMIT+1); + +#define KB *(1 <<10) +#define MB *(1 <<20) +#define GB *(1U<<30) + +#define MAXD_LOG 16 +#define MAX_DISTANCE ((1 << MAXD_LOG) - 1) + +#define ML_BITS 4 +#define ML_MASK ((1U<<ML_BITS)-1) +#define RUN_BITS (8-ML_BITS) +#define RUN_MASK ((1U<<RUN_BITS)-1) + + +/************************************** +* Local Structures and types +**************************************/ +typedef enum { noDict = 0, withPrefix64k, usingExtDict } dict_directive; +typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; +typedef enum { full = 0, partial = 1 } earlyEnd_directive; + + + +/******************************* +* Decompression functions +*******************************/ +/* + * This generic decompression function cover all use cases. + * It shall be instantiated several times, using different sets of directives + * Note that it is essential this generic function is really inlined, + * in order to remove useless branches during compilation optimization. + */ +FORCE_INLINE int LZ4_decompress_generic( + const char* const source, + char* const dest, + int inputSize, + int outputSize, /* If endOnInput==endOnInputSize, this value is the max size of Output Buffer. */ + + int endOnInput, /* endOnOutputSize, endOnInputSize */ + int partialDecoding, /* full, partial */ + int targetOutputSize, /* only used if partialDecoding==partial */ + int dict, /* noDict, withPrefix64k, usingExtDict */ + const BYTE* const lowPrefix, /* == dest if dict == noDict */ + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note : = 0 if noDict */ + ) +{ + /* Local Variables */ + const BYTE* ip = (const BYTE*) source; + const BYTE* const iend = ip + inputSize; + + BYTE* op = (BYTE*) dest; + BYTE* const oend = op + outputSize; + BYTE* cpy; + BYTE* oexit = op + targetOutputSize; + const BYTE* const lowLimit = lowPrefix - dictSize; + + const BYTE* const dictEnd = (const BYTE*)dictStart + dictSize; + const unsigned dec32table[] = {4, 1, 2, 1, 4, 4, 4, 4}; + const int dec64table[] = {0, 0, 0, -1, 0, 1, 2, 3}; + + const int safeDecode = (endOnInput==endOnInputSize); + const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); + const int inPlaceDecode = ((ip >= op) && (ip < oend)); + + + /* Special cases */ + if ((partialDecoding) && (oexit> 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; + size_t offset; + + if (unlikely((inPlaceDecode) && (op + WILDCOPYLENGTH > ip))) goto _output_error; /* output stream ran over input stream */ + + /* get literal length */ + token = *ip++; + if ((length=(token>>ML_BITS)) == RUN_MASK) + { + unsigned s; + do + { + s = *ip++; + length += s; + } + while ( likely(endOnInput ? ip<iend-RUN_MASK : 1) && (s==255) ); + if ((safeDecode) && unlikely((size_t)(op+length)<(size_t)(op))) goto _output_error; /* overflow detection */ + if ((safeDecode) && unlikely((size_t)(ip+length)<(size_t)(ip))) goto _output_error; /* overflow detection */ + } + + /* copy literals */ + cpy = op+length; + if (((endOnInput) && ((cpy>(partialDecoding?oexit:oend-MFLIMIT)) || (ip+length>iend-(2+1+LASTLITERALS))) ) + || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH))) + { + 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 */ + } + memmove(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 */ + offset = LZ4_readLE16(ip); ip+=2; + match = op - offset; + if ((checkOffset) && (unlikely(match < lowLimit))) goto _output_error; /* Error : offset outside buffers */ + + /* 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 block */ + size_t copySize = (size_t)(lowPrefix-match); + memcpy(op, dictEnd - copySize, copySize); + op += copySize; + copySize = length - copySize; + if (copySize > (size_t)(op-lowPrefix)) /* overlap copy */ + { + BYTE* const endOfMatch = op + copySize; + const BYTE* copyFrom = lowPrefix; + while (op < endOfMatch) *op++ = *copyFrom++; + } + else + { + memcpy(op, lowPrefix, copySize); + op += copySize; + } + } + continue; + } + + /* copy match within block */ + cpy = op + length; + if (unlikely(offset<8)) + { + const int dec64 = dec64table[offset]; + op[0] = match[0]; + op[1] = match[1]; + op[2] = match[2]; + op[3] = match[3]; + match += dec32table[offset]; + memcpy(op+4, match, 4); + match -= dec64; + } else { LZ4_copy8(op, match); match+=8; } + op += 8; + + if (unlikely(cpy>oend-12)) + { + BYTE* const oCopyLimit = oend-(WILDCOPYLENGTH-1); + if (cpy > oend-LASTLITERALS) goto _output_error; /* Error : last LASTLITERALS bytes must be literals (uncompressed) */ + if (op < oCopyLimit) + { + LZ4_wildCopy(op, match, oCopyLimit); + match += oCopyLimit - op; + op = oCopyLimit; + } + while (op<cpy) *op++ = *match++; + } + else + LZ4_wildCopy(op, match, cpy); + op=cpy; /* correction */ + } + + /* end of decoding */ + if (endOnInput) + return (int) (((char*)op)-dest); /* Nb of output bytes decoded */ + else + return (int) (((const char*)ip)-source); /* Nb of input bytes read */ + + /* Overflow error detected */ +_output_error: + return (int) (-(((const char*)ip)-source))-1; +} diff --git a/src/commonlib/bsd/lz4_wrapper.c b/src/commonlib/bsd/lz4_wrapper.c new file mode 100644 index 0000000000..2367afceaf --- /dev/null +++ b/src/commonlib/bsd/lz4_wrapper.c @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ + +#include <commonlib/bsd/compression.h> +#include <commonlib/bsd/helpers.h> +#include <endian.h> +#include <stdint.h> +#include <string.h> + +/* LZ4 comes with its own supposedly portable memory access functions, but they + * seem to be very inefficient in practice (at least on ARM64). Since coreboot + * knows about endinaness and allows some basic assumptions (such as unaligned + * access support), we can easily write the ones we need ourselves. */ +static uint16_t LZ4_readLE16(const void *src) +{ + return le16toh(*(const uint16_t *)src); +} +static void LZ4_copy8(void *dst, const void *src) +{ +/* ARM32 needs to be a special snowflake to prevent GCC from coalescing the + * access into LDRD/STRD (which don't support unaligned accesses). */ +#ifdef __arm__ /* ARMv < 6 doesn't support unaligned accesses at all. */ + #if defined(__COREBOOT_ARM_ARCH__) && __COREBOOT_ARM_ARCH__ < 6 + int i; + for (i = 0; i < 8; i++) + ((uint8_t *)dst)[i] = ((uint8_t *)src)[i]; + #else + uint32_t x0, x1; + __asm__ ("ldr %[x0], [%[src]]" + : [x0]"=r"(x0) + : [src]"r"(src), "m"(*(const uint32_t *)src)); + __asm__ ("ldr %[x1], [%[src], #4]" + : [x1]"=r"(x1) + : [src]"r"(src), "m"(*(const uint32_t *)(src + 4))); + __asm__ ("str %[x0], [%[dst]]" + : "=m"(*(uint32_t *)dst) + : [x0]"r"(x0), [dst]"r"(dst)); + __asm__ ("str %[x1], [%[dst], #4]" + : "=m"(*(uint32_t *)(dst + 4)) + : [x1]"r"(x1), [dst]"r"(dst)); + #endif +#elif defined(__riscv) + /* RISC-V implementations may trap on any unaligned access. */ + int i; + for (i = 0; i < 8; i++) + ((uint8_t *)dst)[i] = ((uint8_t *)src)[i]; +#else + *(uint64_t *)dst = *(const uint64_t *)src; +#endif +} + +typedef uint8_t BYTE; +typedef uint16_t U16; +typedef uint32_t U32; +typedef int32_t S32; +typedef uint64_t U64; + +#define FORCE_INLINE static __always_inline +#define likely(expr) __builtin_expect((expr) != 0, 1) +#define unlikely(expr) __builtin_expect((expr) != 0, 0) + +/* Unaltered (just removed unrelated code) from github.com/Cyan4973/lz4/dev. */ +#include "lz4.c.inc" /* #include for inlining, do not link! */ + +#define LZ4F_MAGICNUMBER 0x184D2204 + +struct lz4_frame_header { + uint32_t magic; + union { + uint8_t flags; + struct { + uint8_t reserved0 : 2; + uint8_t has_content_checksum : 1; + uint8_t has_content_size : 1; + uint8_t has_block_checksum : 1; + uint8_t independent_blocks : 1; + uint8_t version : 2; + }; + }; + union { + uint8_t block_descriptor; + struct { + uint8_t reserved1 : 4; + uint8_t max_block_size : 3; + uint8_t reserved2 : 1; + }; + }; + /* + uint64_t content_size iff has_content_size is set */ + /* + uint8_t header_checksum */ +} __packed; + +struct lz4_block_header { + union { + uint32_t raw; + struct { + uint32_t size : 31; + uint32_t not_compressed : 1; + }; + }; + /* + size bytes of data */ + /* + uint32_t block_checksum iff has_block_checksum is set */ +} __packed; + +size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn) +{ + const void *in = src; + void *out = dst; + size_t out_size = 0; + 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(uint64_t) + sizeof(uint8_t)) + 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(uint64_t); + in += sizeof(uint8_t); + } + + while (1) { + struct lz4_block_header b = { + { .raw = le32toh(*(const uint32_t *)in) } + }; + in += sizeof(struct lz4_block_header); + + if ((size_t)(in - src) + b.size > srcn) + break; /* input overrun */ + + if (!b.size) { + out_size = out - dst; + break; /* decompression successful */ + } + + if (b.not_compressed) { + size_t size = MIN((uintptr_t)b.size, (uintptr_t)dst + + dstn - (uintptr_t)out); + memcpy(out, in, size); + if (size < b.size) + break; /* output overrun */ + out += 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) + break; /* decompression error */ + out += ret; + } + + in += b.size; + if (has_block_checksum) + in += sizeof(uint32_t); + } + + return out_size; +} + +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); +} |