xref: /aosp_15_r20/external/coreboot/src/commonlib/bsd/lz4_wrapper.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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  */
LZ4_readLE16(const void * src)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 
LZ4_readLE32(const void * src)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 
LZ4_copy8(void * dst,const void * src)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 
ulz4fn(const void * src,size_t srcn,void * dst,size_t dstn)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 
ulz4f(const void * src,void * dst)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 }
186