lz4_wrapper.c
1 /* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0-only */ 2 3 #include <commonlib/bsd/compression.h> 4 #include <commonlib/bsd/helpers.h> 5 #include <commonlib/bsd/sysincludes.h> 6 #include <stdint.h> 7 #include <string.h> 8 #include <endian.h> 9 10 /* 11 * RISC-V and older ARM architectures do not mandate support for misaligned access. 12 * Our le16toh and friends functions assume misaligned access support. Writing the access 13 * like this causes the compiler to generate instructions using misaligned access (or not) 14 * depending on the architecture. So there is no performance penalty for platforms supporting 15 * misaligned access. 16 */ 17 static uint16_t LZ4_readLE16(const void *src) 18 { 19 return *((const uint8_t *)src + 1) << 8 20 | *(const uint8_t *)src; 21 } 22 23 static uint32_t LZ4_readLE32(const void *src) 24 { 25 return *((const uint8_t *)src + 3) << 24 26 | *((const uint8_t *)src + 2) << 16 27 | *((const uint8_t *)src + 1) << 8 28 | *(const uint8_t *)src; 29 } 30 31 static void LZ4_copy8(void *dst, const void *src) 32 { 33 /* ARM32 needs to be a special snowflake to prevent GCC from coalescing the 34 * access into LDRD/STRD (which don't support unaligned accesses). */ 35 #ifdef __arm__ /* ARMv < 6 doesn't support unaligned accesses at all. */ 36 #if defined(__COREBOOT_ARM_ARCH__) && __COREBOOT_ARM_ARCH__ < 6 37 int i; 38 for (i = 0; i < 8; i++) 39 ((uint8_t *)dst)[i] = ((uint8_t *)src)[i]; 40 #else 41 uint32_t x0, x1; 42 __asm__ ("ldr %[x0], [%[src]]" 43 : [x0]"=r"(x0) 44 : [src]"r"(src), "m"(*(const uint32_t *)src)); 45 __asm__ ("ldr %[x1], [%[src], #4]" 46 : [x1]"=r"(x1) 47 : [src]"r"(src), "m"(*(const uint32_t *)(src + 4))); 48 __asm__ ("str %[x0], [%[dst]]" 49 : "=m"(*(uint32_t *)dst) 50 : [x0]"r"(x0), [dst]"r"(dst)); 51 __asm__ ("str %[x1], [%[dst], #4]" 52 : "=m"(*(uint32_t *)(dst + 4)) 53 : [x1]"r"(x1), [dst]"r"(dst)); 54 #endif 55 #elif defined(__riscv) 56 /* RISC-V implementations may trap on any unaligned access. */ 57 int i; 58 for (i = 0; i < 8; i++) 59 ((uint8_t *)dst)[i] = ((uint8_t *)src)[i]; 60 #else 61 *(uint64_t *)dst = *(const uint64_t *)src; 62 #endif 63 } 64 65 typedef uint8_t BYTE; 66 typedef uint16_t U16; 67 typedef uint32_t U32; 68 typedef int32_t S32; 69 typedef uint64_t U64; 70 71 #define FORCE_INLINE static __always_inline 72 #define likely(expr) __builtin_expect((expr) != 0, 1) 73 #define unlikely(expr) __builtin_expect((expr) != 0, 0) 74 75 /* Unaltered (just removed unrelated code) from github.com/Cyan4973/lz4/dev. */ 76 #include "lz4.c.inc" /* #include for inlining, do not link! */ 77 78 #define LZ4F_MAGICNUMBER 0x184D2204 79 80 /* Bit field endianness is implementation-defined. Use masks instead. 81 * https://stackoverflow.com/a/6044223 */ 82 #define RESERVED0 0x03 83 #define HAS_CONTENT_CHECKSUM 0x04 84 #define HAS_CONTENT_SIZE 0x08 85 #define HAS_BLOCK_CHECKSUM 0x10 86 #define INDEPENDENT_BLOCKS 0x20 87 #define VERSION 0xC0 88 #define VERSION_SHIFT 6 89 90 #define RESERVED1_2 0x8F 91 #define MAX_BLOCK_SIZE 0x70 92 93 struct lz4_frame_header { 94 uint32_t magic; 95 uint8_t flags; 96 uint8_t block_descriptor; 97 /* + uint64_t content_size iff has_content_size is set */ 98 /* + uint8_t header_checksum */ 99 } __packed; 100 101 #define BH_SIZE 0x7FFFFFFF 102 #define NOT_COMPRESSED 0x80000000 103 104 struct lz4_block_header { 105 uint32_t raw; 106 /* + size bytes of data */ 107 /* + uint32_t block_checksum iff has_block_checksum is set */ 108 } __packed; 109 110 size_t ulz4fn(const void *src, size_t srcn, void *dst, size_t dstn) 111 { 112 const void *in = src; 113 void *out = dst; 114 size_t out_size = 0; 115 int has_block_checksum; 116 117 { /* With in-place decompression the header may become invalid later. */ 118 const struct lz4_frame_header *h = in; 119 120 if (srcn < sizeof(*h) + sizeof(uint64_t) + sizeof(uint8_t)) 121 return 0; /* input overrun */ 122 123 /* We assume there's always only a single, standard frame. */ 124 if (LZ4_readLE32(&h->magic) != LZ4F_MAGICNUMBER 125 || (h->flags & VERSION) != (1 << VERSION_SHIFT)) 126 return 0; /* unknown format */ 127 if ((h->flags & RESERVED0) || (h->block_descriptor & RESERVED1_2)) 128 return 0; /* reserved must be zero */ 129 if (!(h->flags & INDEPENDENT_BLOCKS)) 130 return 0; /* we don't support block dependency */ 131 has_block_checksum = h->flags & HAS_BLOCK_CHECKSUM; 132 133 in += sizeof(*h); 134 if (h->flags & HAS_CONTENT_SIZE) 135 in += sizeof(uint64_t); 136 in += sizeof(uint8_t); 137 } 138 139 while (1) { 140 if ((size_t)(in - src) + sizeof(struct lz4_block_header) > srcn) 141 break; /* input overrun */ 142 143 struct lz4_block_header b = { 144 .raw = LZ4_readLE32((const uint32_t *)in) 145 }; 146 in += sizeof(struct lz4_block_header); 147 148 if ((size_t)(in - src) + (b.raw & BH_SIZE) > srcn) 149 break; /* input overrun */ 150 151 if (!(b.raw & BH_SIZE)) { 152 out_size = out - dst; 153 break; /* decompression successful */ 154 } 155 156 if (b.raw & NOT_COMPRESSED) { 157 size_t size = MIN((uintptr_t)(b.raw & BH_SIZE), (uintptr_t)dst 158 + dstn - (uintptr_t)out); 159 memcpy(out, in, size); 160 if (size < (b.raw & BH_SIZE)) 161 break; /* output overrun */ 162 out += size; 163 } else { 164 /* constant folding essential, do not touch params! */ 165 int ret = LZ4_decompress_generic(in, out, (b.raw & BH_SIZE), 166 dst + dstn - out, endOnInputSize, 167 full, 0, noDict, out, NULL, 0); 168 if (ret < 0) 169 break; /* decompression error */ 170 out += ret; 171 } 172 173 in += (b.raw & BH_SIZE); 174 if (has_block_checksum) 175 in += sizeof(uint32_t); 176 } 177 178 return out_size; 179 } 180 181 size_t ulz4f(const void *src, void *dst) 182 { 183 /* LZ4 uses signed size parameters, so can't just use ((u32)-1) here. */ 184 return ulz4fn(src, 1*GiB, dst, 1*GiB); 185 }