]>
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> |
027b728d JW |
9 | #include <linux/kernel.h> |
10 | #include <linux/types.h> | |
e7885a48 | 11 | #include <asm/unaligned.h> |
2a2d8e94 | 12 | #include <u-boot/lz4.h> |
027b728d | 13 | |
e7885a48 | 14 | /* lz4.c is unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */ |
027b728d JW |
15 | #include "lz4.c" /* #include for inlining, do not link! */ |
16 | ||
e7885a48 | 17 | #define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U |
027b728d JW |
18 | |
19 | int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn) | |
20 | { | |
21 | const void *end = dst + *dstn; | |
22 | const void *in = src; | |
23 | void *out = dst; | |
24 | int has_block_checksum; | |
25 | int ret; | |
26 | *dstn = 0; | |
27 | ||
28 | { /* With in-place decompression the header may become invalid later. */ | |
e7885a48 RV |
29 | u32 magic; |
30 | u8 flags, version, independent_blocks, has_content_size; | |
31 | u8 block_desc; | |
027b728d | 32 | |
e7885a48 | 33 | if (srcn < sizeof(u32) + 3*sizeof(u8)) |
027b728d JW |
34 | return -EINVAL; /* input overrun */ |
35 | ||
e7885a48 RV |
36 | magic = get_unaligned_le32(in); |
37 | in += sizeof(u32); | |
38 | flags = *(u8 *)in; | |
39 | in += sizeof(u8); | |
40 | block_desc = *(u8 *)in; | |
41 | in += sizeof(u8); | |
42 | ||
43 | version = (flags >> 6) & 0x3; | |
44 | independent_blocks = (flags >> 5) & 0x1; | |
45 | has_block_checksum = (flags >> 4) & 0x1; | |
46 | has_content_size = (flags >> 3) & 0x1; | |
47 | ||
027b728d | 48 | /* We assume there's always only a single, standard frame. */ |
e7885a48 | 49 | if (magic != LZ4F_MAGIC || version != 1) |
027b728d | 50 | return -EPROTONOSUPPORT; /* unknown format */ |
e7885a48 RV |
51 | if ((flags & 0x03) || (block_desc & 0x8f)) |
52 | return -EINVAL; /* reserved bits must be zero */ | |
53 | if (!independent_blocks) | |
027b728d | 54 | return -EPROTONOSUPPORT; /* we can't support this yet */ |
027b728d | 55 | |
e7885a48 RV |
56 | if (has_content_size) { |
57 | if (srcn < sizeof(u32) + 3*sizeof(u8) + sizeof(u64)) | |
58 | return -EINVAL; /* input overrun */ | |
027b728d | 59 | in += sizeof(u64); |
e7885a48 RV |
60 | } |
61 | /* Header checksum byte */ | |
027b728d JW |
62 | in += sizeof(u8); |
63 | } | |
64 | ||
65 | while (1) { | |
e7885a48 | 66 | u32 block_header, block_size; |
60f989a9 | 67 | |
e7885a48 RV |
68 | block_header = get_unaligned_le32(in); |
69 | in += sizeof(u32); | |
70 | block_size = block_header & ~LZ4F_BLOCKUNCOMPRESSED_FLAG; | |
027b728d | 71 | |
e7885a48 | 72 | if (in - src + block_size > srcn) { |
027b728d JW |
73 | ret = -EINVAL; /* input overrun */ |
74 | break; | |
75 | } | |
76 | ||
e7885a48 | 77 | if (!block_size) { |
027b728d JW |
78 | ret = 0; /* decompression successful */ |
79 | break; | |
80 | } | |
81 | ||
e7885a48 | 82 | if (block_header & LZ4F_BLOCKUNCOMPRESSED_FLAG) { |
3ff4675d | 83 | size_t size = min((ptrdiff_t)block_size, (ptrdiff_t)(end - out)); |
027b728d JW |
84 | memcpy(out, in, size); |
85 | out += size; | |
e7885a48 | 86 | if (size < block_size) { |
027b728d JW |
87 | ret = -ENOBUFS; /* output overrun */ |
88 | break; | |
89 | } | |
90 | } else { | |
91 | /* constant folding essential, do not touch params! */ | |
e7885a48 | 92 | ret = LZ4_decompress_generic(in, out, block_size, |
027b728d | 93 | end - out, endOnInputSize, |
26c7fdad | 94 | decode_full_block, noDict, out, NULL, 0); |
027b728d JW |
95 | if (ret < 0) { |
96 | ret = -EPROTO; /* decompression error */ | |
97 | break; | |
98 | } | |
99 | out += ret; | |
100 | } | |
101 | ||
e7885a48 | 102 | in += block_size; |
027b728d JW |
103 | if (has_block_checksum) |
104 | in += sizeof(u32); | |
105 | } | |
106 | ||
107 | *dstn = out - dst; | |
108 | return ret; | |
109 | } |