Commit | Line | Data |
---|---|---|
4549e789 | 1 | // SPDX-License-Identifier: GPL 2.0+ OR BSD-3-Clause |
027b728d JW |
2 | /* |
3 | * Copyright 2015 Google Inc. | |
027b728d JW |
4 | */ |
5 | ||
6 | #include <common.h> | |
7 | #include <compiler.h> | |
829ceb28 | 8 | #include <image.h> |
6c03f9e6 | 9 | #include <lz4.h> |
027b728d JW |
10 | #include <linux/kernel.h> |
11 | #include <linux/types.h> | |
e7885a48 | 12 | #include <asm/unaligned.h> |
027b728d | 13 | |
227c53de KB |
14 | static u16 LZ4_readLE16(const void *src) |
15 | { | |
16 | return get_unaligned_le16(src); | |
17 | } | |
18 | static void LZ4_copy4(void *dst, const void *src) | |
19 | { | |
20 | put_unaligned(get_unaligned((const u32 *)src), (u32 *)dst); | |
21 | } | |
22 | static void LZ4_copy8(void *dst, const void *src) | |
23 | { | |
24 | put_unaligned(get_unaligned((const u64 *)src), (u64 *)dst); | |
25 | } | |
027b728d JW |
26 | |
27 | typedef uint8_t BYTE; | |
28 | typedef uint16_t U16; | |
29 | typedef uint32_t U32; | |
30 | typedef int32_t S32; | |
31 | typedef uint64_t U64; | |
32 | ||
33 | #define FORCE_INLINE static inline __attribute__((always_inline)) | |
34 | ||
e7885a48 | 35 | /* lz4.c is unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */ |
027b728d JW |
36 | #include "lz4.c" /* #include for inlining, do not link! */ |
37 | ||
e7885a48 | 38 | #define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U |
027b728d JW |
39 | |
40 | int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn) | |
41 | { | |
42 | const void *end = dst + *dstn; | |
43 | const void *in = src; | |
44 | void *out = dst; | |
45 | int has_block_checksum; | |
46 | int ret; | |
47 | *dstn = 0; | |
48 | ||
49 | { /* With in-place decompression the header may become invalid later. */ | |
e7885a48 RV |
50 | u32 magic; |
51 | u8 flags, version, independent_blocks, has_content_size; | |
52 | u8 block_desc; | |
027b728d | 53 | |
e7885a48 | 54 | if (srcn < sizeof(u32) + 3*sizeof(u8)) |
027b728d JW |
55 | return -EINVAL; /* input overrun */ |
56 | ||
e7885a48 RV |
57 | magic = get_unaligned_le32(in); |
58 | in += sizeof(u32); | |
59 | flags = *(u8 *)in; | |
60 | in += sizeof(u8); | |
61 | block_desc = *(u8 *)in; | |
62 | in += sizeof(u8); | |
63 | ||
64 | version = (flags >> 6) & 0x3; | |
65 | independent_blocks = (flags >> 5) & 0x1; | |
66 | has_block_checksum = (flags >> 4) & 0x1; | |
67 | has_content_size = (flags >> 3) & 0x1; | |
68 | ||
027b728d | 69 | /* We assume there's always only a single, standard frame. */ |
e7885a48 | 70 | if (magic != LZ4F_MAGIC || version != 1) |
027b728d | 71 | return -EPROTONOSUPPORT; /* unknown format */ |
e7885a48 RV |
72 | if ((flags & 0x03) || (block_desc & 0x8f)) |
73 | return -EINVAL; /* reserved bits must be zero */ | |
74 | if (!independent_blocks) | |
027b728d | 75 | return -EPROTONOSUPPORT; /* we can't support this yet */ |
027b728d | 76 | |
e7885a48 RV |
77 | if (has_content_size) { |
78 | if (srcn < sizeof(u32) + 3*sizeof(u8) + sizeof(u64)) | |
79 | return -EINVAL; /* input overrun */ | |
027b728d | 80 | in += sizeof(u64); |
e7885a48 RV |
81 | } |
82 | /* Header checksum byte */ | |
027b728d JW |
83 | in += sizeof(u8); |
84 | } | |
85 | ||
86 | while (1) { | |
e7885a48 | 87 | u32 block_header, block_size; |
60f989a9 | 88 | |
e7885a48 RV |
89 | block_header = get_unaligned_le32(in); |
90 | in += sizeof(u32); | |
91 | block_size = block_header & ~LZ4F_BLOCKUNCOMPRESSED_FLAG; | |
027b728d | 92 | |
e7885a48 | 93 | if (in - src + block_size > srcn) { |
027b728d JW |
94 | ret = -EINVAL; /* input overrun */ |
95 | break; | |
96 | } | |
97 | ||
e7885a48 | 98 | if (!block_size) { |
027b728d JW |
99 | ret = 0; /* decompression successful */ |
100 | break; | |
101 | } | |
102 | ||
e7885a48 RV |
103 | if (block_header & LZ4F_BLOCKUNCOMPRESSED_FLAG) { |
104 | size_t size = min((ptrdiff_t)block_size, end - out); | |
027b728d JW |
105 | memcpy(out, in, size); |
106 | out += size; | |
e7885a48 | 107 | if (size < block_size) { |
027b728d JW |
108 | ret = -ENOBUFS; /* output overrun */ |
109 | break; | |
110 | } | |
111 | } else { | |
112 | /* constant folding essential, do not touch params! */ | |
e7885a48 | 113 | ret = LZ4_decompress_generic(in, out, block_size, |
027b728d JW |
114 | end - out, endOnInputSize, |
115 | full, 0, noDict, out, NULL, 0); | |
116 | if (ret < 0) { | |
117 | ret = -EPROTO; /* decompression error */ | |
118 | break; | |
119 | } | |
120 | out += ret; | |
121 | } | |
122 | ||
e7885a48 | 123 | in += block_size; |
027b728d JW |
124 | if (has_block_checksum) |
125 | in += sizeof(u32); | |
126 | } | |
127 | ||
128 | *dstn = out - dst; | |
129 | return ret; | |
130 | } |