Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : : /*
3 : : * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd
4 : : *
5 : : * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
6 : : */
7 : :
8 : : #ifdef STATIC
9 : : #define PREBOOT
10 : : #include "lz4/lz4_decompress.c"
11 : : #else
12 : : #include <linux/decompress/unlz4.h>
13 : : #endif
14 : : #include <linux/types.h>
15 : : #include <linux/lz4.h>
16 : : #include <linux/decompress/mm.h>
17 : : #include <linux/compiler.h>
18 : :
19 : : #include <asm/unaligned.h>
20 : :
21 : : /*
22 : : * Note: Uncompressed chunk size is used in the compressor side
23 : : * (userspace side for compression).
24 : : * It is hardcoded because there is not proper way to extract it
25 : : * from the binary stream which is generated by the preliminary
26 : : * version of LZ4 tool so far.
27 : : */
28 : : #define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20)
29 : : #define ARCHIVE_MAGICNUMBER 0x184C2102
30 : :
31 : 0 : STATIC inline int INIT unlz4(u8 *input, long in_len,
32 : : long (*fill)(void *, unsigned long),
33 : : long (*flush)(void *, unsigned long),
34 : : u8 *output, long *posp,
35 : : void (*error) (char *x))
36 : : {
37 : : int ret = -1;
38 : : size_t chunksize = 0;
39 : : size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE;
40 : : u8 *inp;
41 : : u8 *inp_start;
42 : : u8 *outp;
43 : : long size = in_len;
44 : : #ifdef PREBOOT
45 : : size_t out_len = get_unaligned_le32(input + in_len);
46 : : #endif
47 : : size_t dest_len;
48 : :
49 : :
50 [ # # ]: 0 : if (output) {
51 : : outp = output;
52 [ # # ]: 0 : } else if (!flush) {
53 : 0 : error("NULL output pointer and no flush function provided");
54 : 0 : goto exit_0;
55 : : } else {
56 : 0 : outp = large_malloc(uncomp_chunksize);
57 [ # # ]: 0 : if (!outp) {
58 : 0 : error("Could not allocate output buffer");
59 : 0 : goto exit_0;
60 : : }
61 : : }
62 : :
63 [ # # ]: 0 : if (input && fill) {
64 : 0 : error("Both input pointer and fill function provided,");
65 : 0 : goto exit_1;
66 [ # # ]: 0 : } else if (input) {
67 : : inp = input;
68 [ # # ]: 0 : } else if (!fill) {
69 : 0 : error("NULL input pointer and missing fill function");
70 : 0 : goto exit_1;
71 : : } else {
72 : 0 : inp = large_malloc(LZ4_compressBound(uncomp_chunksize));
73 [ # # ]: 0 : if (!inp) {
74 : 0 : error("Could not allocate input buffer");
75 : 0 : goto exit_1;
76 : : }
77 : : }
78 : : inp_start = inp;
79 : :
80 [ # # ]: 0 : if (posp)
81 : 0 : *posp = 0;
82 : :
83 [ # # ]: 0 : if (fill) {
84 : 0 : size = fill(inp, 4);
85 [ # # ]: 0 : if (size < 4) {
86 : 0 : error("data corrupted");
87 : 0 : goto exit_2;
88 : : }
89 : : }
90 : :
91 : : chunksize = get_unaligned_le32(inp);
92 [ # # ]: 0 : if (chunksize == ARCHIVE_MAGICNUMBER) {
93 [ # # ]: 0 : if (!fill) {
94 : 0 : inp += 4;
95 : 0 : size -= 4;
96 : : }
97 : : } else {
98 : 0 : error("invalid header");
99 : 0 : goto exit_2;
100 : : }
101 : :
102 [ # # ]: 0 : if (posp)
103 : 0 : *posp += 4;
104 : :
105 : : for (;;) {
106 : :
107 [ # # ]: 0 : if (fill) {
108 : 0 : size = fill(inp, 4);
109 [ # # ]: 0 : if (size == 0)
110 : : break;
111 [ # # ]: 0 : if (size < 4) {
112 : 0 : error("data corrupted");
113 : 0 : goto exit_2;
114 : : }
115 : : }
116 : :
117 : : chunksize = get_unaligned_le32(inp);
118 [ # # ]: 0 : if (chunksize == ARCHIVE_MAGICNUMBER) {
119 [ # # ]: 0 : if (!fill) {
120 : 0 : inp += 4;
121 : 0 : size -= 4;
122 : : }
123 [ # # ]: 0 : if (posp)
124 : 0 : *posp += 4;
125 : 0 : continue;
126 : : }
127 : :
128 : :
129 [ # # ]: 0 : if (posp)
130 : 0 : *posp += 4;
131 : :
132 [ # # ]: 0 : if (!fill) {
133 : 0 : inp += 4;
134 : 0 : size -= 4;
135 : : } else {
136 [ # # ]: 0 : if (chunksize > LZ4_compressBound(uncomp_chunksize)) {
137 : 0 : error("chunk length is longer than allocated");
138 : 0 : goto exit_2;
139 : : }
140 : 0 : size = fill(inp, chunksize);
141 [ # # ]: 0 : if (size < chunksize) {
142 : 0 : error("data corrupted");
143 : 0 : goto exit_2;
144 : : }
145 : : }
146 : : #ifdef PREBOOT
147 : : if (out_len >= uncomp_chunksize) {
148 : : dest_len = uncomp_chunksize;
149 : : out_len -= dest_len;
150 : : } else
151 : : dest_len = out_len;
152 : :
153 : : ret = LZ4_decompress_fast(inp, outp, dest_len);
154 : : chunksize = ret;
155 : : #else
156 : : dest_len = uncomp_chunksize;
157 : :
158 : 0 : ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len);
159 : 0 : dest_len = ret;
160 : : #endif
161 [ # # ]: 0 : if (ret < 0) {
162 : 0 : error("Decoding failed");
163 : 0 : goto exit_2;
164 : : }
165 : :
166 : : ret = -1;
167 [ # # # # ]: 0 : if (flush && flush(outp, dest_len) != dest_len)
168 : : goto exit_2;
169 [ # # ]: 0 : if (output)
170 : 0 : outp += dest_len;
171 [ # # ]: 0 : if (posp)
172 : 0 : *posp += chunksize;
173 : :
174 [ # # ]: 0 : if (!fill) {
175 : 0 : size -= chunksize;
176 : :
177 [ # # ]: 0 : if (size == 0)
178 : : break;
179 [ # # ]: 0 : else if (size < 0) {
180 : 0 : error("data corrupted");
181 : 0 : goto exit_2;
182 : : }
183 : 0 : inp += chunksize;
184 : : }
185 : : }
186 : :
187 : : ret = 0;
188 : : exit_2:
189 [ # # ]: 0 : if (!input)
190 : 0 : large_free(inp_start);
191 : : exit_1:
192 [ # # ]: 0 : if (!output)
193 : 0 : large_free(outp);
194 : : exit_0:
195 : 0 : return ret;
196 : : }
197 : :
198 : : #ifdef PREBOOT
199 : : STATIC int INIT __decompress(unsigned char *buf, long in_len,
200 : : long (*fill)(void*, unsigned long),
201 : : long (*flush)(void*, unsigned long),
202 : : unsigned char *output, long out_len,
203 : : long *posp,
204 : : void (*error)(char *x)
205 : : )
206 : : {
207 : : return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
208 : : }
209 : : #endif
|