Commit 3d4ef38de2f65e9d122e1c40221f303ade0eac97
Committed by
Tom Rini
1 parent
6fb77c48e4
Exists in
v2017.01-smarct4x
and in
30 other branches
sparse: Rename the file and header
The Android sparse image format is currently supported through a file called aboot, which isn't really such a great name, since the sparse image format is only used for transferring data with fastboot. Rename the file and header to a file called "sparse", which also makes it consistent with the header defining the image structures. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> Reviewed-by: Tom Rini <trini@konsulko.com>
Showing 7 changed files with 431 additions and 431 deletions Side-by-side Diff
common/Makefile
common/aboot.c
1 | -/* | |
2 | - * Copyright (c) 2009, Google Inc. | |
3 | - * All rights reserved. | |
4 | - * | |
5 | - * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. | |
6 | - * Portions Copyright 2014 Broadcom Corporation. | |
7 | - * | |
8 | - * Redistribution and use in source and binary forms, with or without | |
9 | - * modification, are permitted provided that the following conditions are met: | |
10 | - * * Redistributions of source code must retain the above copyright | |
11 | - * notice, this list of conditions and the following disclaimer. | |
12 | - * * Redistributions in binary form must reproduce the above copyright | |
13 | - * notice, this list of conditions and the following disclaimer in the | |
14 | - * documentation and/or other materials provided with the distribution. | |
15 | - * * Neither the name of The Linux Foundation nor | |
16 | - * the names of its contributors may be used to endorse or promote | |
17 | - * products derived from this software without specific prior written | |
18 | - * permission. | |
19 | - * | |
20 | - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
21 | - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
22 | - * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
23 | - * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
24 | - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
25 | - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
26 | - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
27 | - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
28 | - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
29 | - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
30 | - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | - * | |
32 | - * NOTE: | |
33 | - * Although it is very similar, this license text is not identical | |
34 | - * to the "BSD-3-Clause", therefore, DO NOT MODIFY THIS LICENSE TEXT! | |
35 | - */ | |
36 | - | |
37 | -#include <config.h> | |
38 | -#include <common.h> | |
39 | -#include <aboot.h> | |
40 | -#include <div64.h> | |
41 | -#include <errno.h> | |
42 | -#include <malloc.h> | |
43 | -#include <part.h> | |
44 | -#include <sparse_format.h> | |
45 | - | |
46 | -#include <linux/math64.h> | |
47 | - | |
48 | -typedef struct sparse_buffer { | |
49 | - void *data; | |
50 | - u32 length; | |
51 | - u32 repeat; | |
52 | - u16 type; | |
53 | -} sparse_buffer_t; | |
54 | - | |
55 | -static uint32_t last_offset; | |
56 | - | |
57 | -static unsigned int sparse_get_chunk_data_size(sparse_header_t *sparse, | |
58 | - chunk_header_t *chunk) | |
59 | -{ | |
60 | - return chunk->total_sz - sparse->chunk_hdr_sz; | |
61 | -} | |
62 | - | |
63 | -static unsigned int sparse_block_size_to_storage(unsigned int size, | |
64 | - sparse_storage_t *storage, | |
65 | - sparse_header_t *sparse) | |
66 | -{ | |
67 | - return size * sparse->blk_sz / storage->block_sz; | |
68 | -} | |
69 | - | |
70 | -static bool sparse_chunk_has_buffer(chunk_header_t *chunk) | |
71 | -{ | |
72 | - switch (chunk->chunk_type) { | |
73 | - case CHUNK_TYPE_RAW: | |
74 | - case CHUNK_TYPE_FILL: | |
75 | - return true; | |
76 | - | |
77 | - default: | |
78 | - return false; | |
79 | - } | |
80 | -} | |
81 | - | |
82 | -static sparse_header_t *sparse_parse_header(void **data) | |
83 | -{ | |
84 | - /* Read and skip over sparse image header */ | |
85 | - sparse_header_t *sparse_header = (sparse_header_t *) *data; | |
86 | - | |
87 | - *data += sparse_header->file_hdr_sz; | |
88 | - | |
89 | - debug("=== Sparse Image Header ===\n"); | |
90 | - debug("magic: 0x%x\n", sparse_header->magic); | |
91 | - debug("major_version: 0x%x\n", sparse_header->major_version); | |
92 | - debug("minor_version: 0x%x\n", sparse_header->minor_version); | |
93 | - debug("file_hdr_sz: %d\n", sparse_header->file_hdr_sz); | |
94 | - debug("chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz); | |
95 | - debug("blk_sz: %d\n", sparse_header->blk_sz); | |
96 | - debug("total_blks: %d\n", sparse_header->total_blks); | |
97 | - debug("total_chunks: %d\n", sparse_header->total_chunks); | |
98 | - | |
99 | - return sparse_header; | |
100 | -} | |
101 | - | |
102 | -static int sparse_parse_fill_chunk(sparse_header_t *sparse, | |
103 | - chunk_header_t *chunk) | |
104 | -{ | |
105 | - unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); | |
106 | - | |
107 | - if (chunk_data_sz != sizeof(uint32_t)) | |
108 | - return -EINVAL; | |
109 | - | |
110 | - return 0; | |
111 | -} | |
112 | - | |
113 | -static int sparse_parse_raw_chunk(sparse_header_t *sparse, | |
114 | - chunk_header_t *chunk) | |
115 | -{ | |
116 | - unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); | |
117 | - | |
118 | - /* Check if the data size is a multiple of the main block size */ | |
119 | - if (chunk_data_sz % sparse->blk_sz) | |
120 | - return -EINVAL; | |
121 | - | |
122 | - /* Check that the chunk size is consistent */ | |
123 | - if ((chunk_data_sz / sparse->blk_sz) != chunk->chunk_sz) | |
124 | - return -EINVAL; | |
125 | - | |
126 | - return 0; | |
127 | -} | |
128 | - | |
129 | -static chunk_header_t *sparse_parse_chunk(sparse_header_t *sparse, | |
130 | - void **image) | |
131 | -{ | |
132 | - chunk_header_t *chunk = (chunk_header_t *) *image; | |
133 | - int ret; | |
134 | - | |
135 | - debug("=== Chunk Header ===\n"); | |
136 | - debug("chunk_type: 0x%x\n", chunk->chunk_type); | |
137 | - debug("chunk_data_sz: 0x%x\n", chunk->chunk_sz); | |
138 | - debug("total_size: 0x%x\n", chunk->total_sz); | |
139 | - | |
140 | - switch (chunk->chunk_type) { | |
141 | - case CHUNK_TYPE_RAW: | |
142 | - ret = sparse_parse_raw_chunk(sparse, chunk); | |
143 | - if (ret) | |
144 | - return NULL; | |
145 | - break; | |
146 | - | |
147 | - case CHUNK_TYPE_FILL: | |
148 | - ret = sparse_parse_fill_chunk(sparse, chunk); | |
149 | - if (ret) | |
150 | - return NULL; | |
151 | - break; | |
152 | - | |
153 | - case CHUNK_TYPE_DONT_CARE: | |
154 | - case CHUNK_TYPE_CRC32: | |
155 | - debug("Ignoring chunk\n"); | |
156 | - break; | |
157 | - | |
158 | - default: | |
159 | - printf("%s: Unknown chunk type: %x\n", __func__, | |
160 | - chunk->chunk_type); | |
161 | - return NULL; | |
162 | - } | |
163 | - | |
164 | - *image += sparse->chunk_hdr_sz; | |
165 | - | |
166 | - return chunk; | |
167 | -} | |
168 | - | |
169 | -static int sparse_get_fill_buffer(sparse_header_t *sparse, | |
170 | - chunk_header_t *chunk, | |
171 | - sparse_buffer_t *buffer, | |
172 | - unsigned int blk_sz, | |
173 | - void *data) | |
174 | -{ | |
175 | - int i; | |
176 | - | |
177 | - buffer->type = CHUNK_TYPE_FILL; | |
178 | - | |
179 | - /* | |
180 | - * We create a buffer of one block, and ask it to be | |
181 | - * repeated as many times as needed. | |
182 | - */ | |
183 | - buffer->length = blk_sz; | |
184 | - buffer->repeat = (chunk->chunk_sz * sparse->blk_sz) / blk_sz; | |
185 | - | |
186 | - buffer->data = memalign(ARCH_DMA_MINALIGN, | |
187 | - ROUNDUP(blk_sz, | |
188 | - ARCH_DMA_MINALIGN)); | |
189 | - if (!buffer->data) | |
190 | - return -ENOMEM; | |
191 | - | |
192 | - for (i = 0; i < (buffer->length / sizeof(uint32_t)); i++) | |
193 | - ((uint32_t *)buffer->data)[i] = *(uint32_t *)(data); | |
194 | - | |
195 | - return 0; | |
196 | -} | |
197 | - | |
198 | -static int sparse_get_raw_buffer(sparse_header_t *sparse, | |
199 | - chunk_header_t *chunk, | |
200 | - sparse_buffer_t *buffer, | |
201 | - unsigned int blk_sz, | |
202 | - void *data) | |
203 | -{ | |
204 | - unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); | |
205 | - | |
206 | - buffer->type = CHUNK_TYPE_RAW; | |
207 | - buffer->length = chunk_data_sz; | |
208 | - buffer->data = data; | |
209 | - buffer->repeat = 1; | |
210 | - | |
211 | - return 0; | |
212 | -} | |
213 | - | |
214 | -static sparse_buffer_t *sparse_get_data_buffer(sparse_header_t *sparse, | |
215 | - chunk_header_t *chunk, | |
216 | - unsigned int blk_sz, | |
217 | - void **image) | |
218 | -{ | |
219 | - unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); | |
220 | - sparse_buffer_t *buffer; | |
221 | - void *data = *image; | |
222 | - int ret; | |
223 | - | |
224 | - *image += chunk_data_sz; | |
225 | - | |
226 | - if (!sparse_chunk_has_buffer(chunk)) | |
227 | - return NULL; | |
228 | - | |
229 | - buffer = calloc(sizeof(sparse_buffer_t), 1); | |
230 | - if (!buffer) | |
231 | - return NULL; | |
232 | - | |
233 | - switch (chunk->chunk_type) { | |
234 | - case CHUNK_TYPE_RAW: | |
235 | - ret = sparse_get_raw_buffer(sparse, chunk, buffer, blk_sz, | |
236 | - data); | |
237 | - if (ret) | |
238 | - return NULL; | |
239 | - break; | |
240 | - | |
241 | - case CHUNK_TYPE_FILL: | |
242 | - ret = sparse_get_fill_buffer(sparse, chunk, buffer, blk_sz, | |
243 | - data); | |
244 | - if (ret) | |
245 | - return NULL; | |
246 | - break; | |
247 | - | |
248 | - default: | |
249 | - return NULL; | |
250 | - } | |
251 | - | |
252 | - debug("=== Buffer ===\n"); | |
253 | - debug("length: 0x%x\n", buffer->length); | |
254 | - debug("repeat: 0x%x\n", buffer->repeat); | |
255 | - debug("type: 0x%x\n", buffer->type); | |
256 | - debug("data: 0x%p\n", buffer->data); | |
257 | - | |
258 | - return buffer; | |
259 | -} | |
260 | - | |
261 | -static void sparse_put_data_buffer(sparse_buffer_t *buffer) | |
262 | -{ | |
263 | - if (buffer->type == CHUNK_TYPE_FILL) | |
264 | - free(buffer->data); | |
265 | - | |
266 | - free(buffer); | |
267 | -} | |
268 | - | |
269 | -int store_sparse_image(sparse_storage_t *storage, void *storage_priv, | |
270 | - unsigned int session_id, void *data) | |
271 | -{ | |
272 | - unsigned int chunk, offset; | |
273 | - sparse_header_t *sparse_header; | |
274 | - chunk_header_t *chunk_header; | |
275 | - sparse_buffer_t *buffer; | |
276 | - uint32_t start; | |
277 | - uint32_t total_blocks = 0; | |
278 | - uint32_t skipped = 0; | |
279 | - int i; | |
280 | - | |
281 | - debug("=== Storage ===\n"); | |
282 | - debug("name: %s\n", storage->name); | |
283 | - debug("block_size: 0x%x\n", storage->block_sz); | |
284 | - debug("start: 0x%x\n", storage->start); | |
285 | - debug("size: 0x%x\n", storage->size); | |
286 | - debug("write: 0x%p\n", storage->write); | |
287 | - debug("priv: 0x%p\n", storage_priv); | |
288 | - | |
289 | - sparse_header = sparse_parse_header(&data); | |
290 | - if (!sparse_header) { | |
291 | - printf("sparse header issue\n"); | |
292 | - return -EINVAL; | |
293 | - } | |
294 | - | |
295 | - /* | |
296 | - * Verify that the sparse block size is a multiple of our | |
297 | - * storage backend block size | |
298 | - */ | |
299 | - div_u64_rem(sparse_header->blk_sz, storage->block_sz, &offset); | |
300 | - if (offset) { | |
301 | - printf("%s: Sparse image block size issue [%u]\n", | |
302 | - __func__, sparse_header->blk_sz); | |
303 | - return -EINVAL; | |
304 | - } | |
305 | - | |
306 | - /* | |
307 | - * If it's a new flashing session, start at the beginning of | |
308 | - * the partition. If not, then simply resume where we were. | |
309 | - */ | |
310 | - if (session_id > 0) | |
311 | - start = last_offset; | |
312 | - else | |
313 | - start = storage->start; | |
314 | - | |
315 | - printf("Flashing sparse image on partition %s at offset 0x%x (ID: %d)\n", | |
316 | - storage->name, start * storage->block_sz, session_id); | |
317 | - | |
318 | - /* Start processing chunks */ | |
319 | - for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) { | |
320 | - uint32_t blkcnt; | |
321 | - | |
322 | - chunk_header = sparse_parse_chunk(sparse_header, &data); | |
323 | - if (!chunk_header) { | |
324 | - printf("Unknown chunk type"); | |
325 | - return -EINVAL; | |
326 | - } | |
327 | - | |
328 | - /* | |
329 | - * If we have a DONT_CARE type, just skip the blocks | |
330 | - * and go on parsing the rest of the chunks | |
331 | - */ | |
332 | - if (chunk_header->chunk_type == CHUNK_TYPE_DONT_CARE) { | |
333 | - skipped += sparse_block_size_to_storage(chunk_header->chunk_sz, | |
334 | - storage, | |
335 | - sparse_header); | |
336 | - continue; | |
337 | - } | |
338 | - | |
339 | - /* Retrieve the buffer we're going to write */ | |
340 | - buffer = sparse_get_data_buffer(sparse_header, chunk_header, | |
341 | - storage->block_sz, &data); | |
342 | - if (!buffer) | |
343 | - continue; | |
344 | - | |
345 | - blkcnt = (buffer->length / storage->block_sz) * buffer->repeat; | |
346 | - | |
347 | - if ((start + total_blocks + blkcnt) > | |
348 | - (storage->start + storage->size)) { | |
349 | - printf("%s: Request would exceed partition size!\n", | |
350 | - __func__); | |
351 | - return -EINVAL; | |
352 | - } | |
353 | - | |
354 | - for (i = 0; i < buffer->repeat; i++) { | |
355 | - unsigned long buffer_blk_cnt; | |
356 | - int ret; | |
357 | - | |
358 | - buffer_blk_cnt = buffer->length / storage->block_sz; | |
359 | - | |
360 | - ret = storage->write(storage, storage_priv, | |
361 | - start + total_blocks, | |
362 | - buffer_blk_cnt, | |
363 | - buffer->data); | |
364 | - if (ret < 0) { | |
365 | - printf("%s: Write %d failed %d\n", | |
366 | - __func__, i, ret); | |
367 | - return ret; | |
368 | - } | |
369 | - | |
370 | - total_blocks += ret; | |
371 | - } | |
372 | - | |
373 | - sparse_put_data_buffer(buffer); | |
374 | - } | |
375 | - | |
376 | - debug("Wrote %d blocks, skipped %d, expected to write %d blocks\n", | |
377 | - total_blocks, skipped, | |
378 | - sparse_block_size_to_storage(sparse_header->total_blks, | |
379 | - storage, sparse_header)); | |
380 | - printf("........ wrote %d blocks to '%s'\n", total_blocks, | |
381 | - storage->name); | |
382 | - | |
383 | - if ((total_blocks + skipped) != | |
384 | - sparse_block_size_to_storage(sparse_header->total_blks, | |
385 | - storage, sparse_header)) { | |
386 | - printf("sparse image write failure\n"); | |
387 | - return -EIO; | |
388 | - } | |
389 | - | |
390 | - last_offset = start + total_blocks; | |
391 | - | |
392 | - return 0; | |
393 | -} |
common/fb_mmc.c
common/fb_nand.c
common/image-sparse.c
1 | +/* | |
2 | + * Copyright (c) 2009, Google Inc. | |
3 | + * All rights reserved. | |
4 | + * | |
5 | + * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved. | |
6 | + * Portions Copyright 2014 Broadcom Corporation. | |
7 | + * | |
8 | + * Redistribution and use in source and binary forms, with or without | |
9 | + * modification, are permitted provided that the following conditions are met: | |
10 | + * * Redistributions of source code must retain the above copyright | |
11 | + * notice, this list of conditions and the following disclaimer. | |
12 | + * * Redistributions in binary form must reproduce the above copyright | |
13 | + * notice, this list of conditions and the following disclaimer in the | |
14 | + * documentation and/or other materials provided with the distribution. | |
15 | + * * Neither the name of The Linux Foundation nor | |
16 | + * the names of its contributors may be used to endorse or promote | |
17 | + * products derived from this software without specific prior written | |
18 | + * permission. | |
19 | + * | |
20 | + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
21 | + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
22 | + * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
23 | + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR | |
24 | + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
25 | + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
26 | + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
27 | + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
28 | + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
29 | + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
30 | + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
31 | + * | |
32 | + * NOTE: | |
33 | + * Although it is very similar, this license text is not identical | |
34 | + * to the "BSD-3-Clause", therefore, DO NOT MODIFY THIS LICENSE TEXT! | |
35 | + */ | |
36 | + | |
37 | +#include <config.h> | |
38 | +#include <common.h> | |
39 | +#include <div64.h> | |
40 | +#include <errno.h> | |
41 | +#include <image-sparse.h> | |
42 | +#include <malloc.h> | |
43 | +#include <part.h> | |
44 | +#include <sparse_format.h> | |
45 | + | |
46 | +#include <linux/math64.h> | |
47 | + | |
48 | +typedef struct sparse_buffer { | |
49 | + void *data; | |
50 | + u32 length; | |
51 | + u32 repeat; | |
52 | + u16 type; | |
53 | +} sparse_buffer_t; | |
54 | + | |
55 | +static uint32_t last_offset; | |
56 | + | |
57 | +static unsigned int sparse_get_chunk_data_size(sparse_header_t *sparse, | |
58 | + chunk_header_t *chunk) | |
59 | +{ | |
60 | + return chunk->total_sz - sparse->chunk_hdr_sz; | |
61 | +} | |
62 | + | |
63 | +static unsigned int sparse_block_size_to_storage(unsigned int size, | |
64 | + sparse_storage_t *storage, | |
65 | + sparse_header_t *sparse) | |
66 | +{ | |
67 | + return size * sparse->blk_sz / storage->block_sz; | |
68 | +} | |
69 | + | |
70 | +static bool sparse_chunk_has_buffer(chunk_header_t *chunk) | |
71 | +{ | |
72 | + switch (chunk->chunk_type) { | |
73 | + case CHUNK_TYPE_RAW: | |
74 | + case CHUNK_TYPE_FILL: | |
75 | + return true; | |
76 | + | |
77 | + default: | |
78 | + return false; | |
79 | + } | |
80 | +} | |
81 | + | |
82 | +static sparse_header_t *sparse_parse_header(void **data) | |
83 | +{ | |
84 | + /* Read and skip over sparse image header */ | |
85 | + sparse_header_t *sparse_header = (sparse_header_t *) *data; | |
86 | + | |
87 | + *data += sparse_header->file_hdr_sz; | |
88 | + | |
89 | + debug("=== Sparse Image Header ===\n"); | |
90 | + debug("magic: 0x%x\n", sparse_header->magic); | |
91 | + debug("major_version: 0x%x\n", sparse_header->major_version); | |
92 | + debug("minor_version: 0x%x\n", sparse_header->minor_version); | |
93 | + debug("file_hdr_sz: %d\n", sparse_header->file_hdr_sz); | |
94 | + debug("chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz); | |
95 | + debug("blk_sz: %d\n", sparse_header->blk_sz); | |
96 | + debug("total_blks: %d\n", sparse_header->total_blks); | |
97 | + debug("total_chunks: %d\n", sparse_header->total_chunks); | |
98 | + | |
99 | + return sparse_header; | |
100 | +} | |
101 | + | |
102 | +static int sparse_parse_fill_chunk(sparse_header_t *sparse, | |
103 | + chunk_header_t *chunk) | |
104 | +{ | |
105 | + unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); | |
106 | + | |
107 | + if (chunk_data_sz != sizeof(uint32_t)) | |
108 | + return -EINVAL; | |
109 | + | |
110 | + return 0; | |
111 | +} | |
112 | + | |
113 | +static int sparse_parse_raw_chunk(sparse_header_t *sparse, | |
114 | + chunk_header_t *chunk) | |
115 | +{ | |
116 | + unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); | |
117 | + | |
118 | + /* Check if the data size is a multiple of the main block size */ | |
119 | + if (chunk_data_sz % sparse->blk_sz) | |
120 | + return -EINVAL; | |
121 | + | |
122 | + /* Check that the chunk size is consistent */ | |
123 | + if ((chunk_data_sz / sparse->blk_sz) != chunk->chunk_sz) | |
124 | + return -EINVAL; | |
125 | + | |
126 | + return 0; | |
127 | +} | |
128 | + | |
129 | +static chunk_header_t *sparse_parse_chunk(sparse_header_t *sparse, | |
130 | + void **image) | |
131 | +{ | |
132 | + chunk_header_t *chunk = (chunk_header_t *) *image; | |
133 | + int ret; | |
134 | + | |
135 | + debug("=== Chunk Header ===\n"); | |
136 | + debug("chunk_type: 0x%x\n", chunk->chunk_type); | |
137 | + debug("chunk_data_sz: 0x%x\n", chunk->chunk_sz); | |
138 | + debug("total_size: 0x%x\n", chunk->total_sz); | |
139 | + | |
140 | + switch (chunk->chunk_type) { | |
141 | + case CHUNK_TYPE_RAW: | |
142 | + ret = sparse_parse_raw_chunk(sparse, chunk); | |
143 | + if (ret) | |
144 | + return NULL; | |
145 | + break; | |
146 | + | |
147 | + case CHUNK_TYPE_FILL: | |
148 | + ret = sparse_parse_fill_chunk(sparse, chunk); | |
149 | + if (ret) | |
150 | + return NULL; | |
151 | + break; | |
152 | + | |
153 | + case CHUNK_TYPE_DONT_CARE: | |
154 | + case CHUNK_TYPE_CRC32: | |
155 | + debug("Ignoring chunk\n"); | |
156 | + break; | |
157 | + | |
158 | + default: | |
159 | + printf("%s: Unknown chunk type: %x\n", __func__, | |
160 | + chunk->chunk_type); | |
161 | + return NULL; | |
162 | + } | |
163 | + | |
164 | + *image += sparse->chunk_hdr_sz; | |
165 | + | |
166 | + return chunk; | |
167 | +} | |
168 | + | |
169 | +static int sparse_get_fill_buffer(sparse_header_t *sparse, | |
170 | + chunk_header_t *chunk, | |
171 | + sparse_buffer_t *buffer, | |
172 | + unsigned int blk_sz, | |
173 | + void *data) | |
174 | +{ | |
175 | + int i; | |
176 | + | |
177 | + buffer->type = CHUNK_TYPE_FILL; | |
178 | + | |
179 | + /* | |
180 | + * We create a buffer of one block, and ask it to be | |
181 | + * repeated as many times as needed. | |
182 | + */ | |
183 | + buffer->length = blk_sz; | |
184 | + buffer->repeat = (chunk->chunk_sz * sparse->blk_sz) / blk_sz; | |
185 | + | |
186 | + buffer->data = memalign(ARCH_DMA_MINALIGN, | |
187 | + ROUNDUP(blk_sz, | |
188 | + ARCH_DMA_MINALIGN)); | |
189 | + if (!buffer->data) | |
190 | + return -ENOMEM; | |
191 | + | |
192 | + for (i = 0; i < (buffer->length / sizeof(uint32_t)); i++) | |
193 | + ((uint32_t *)buffer->data)[i] = *(uint32_t *)(data); | |
194 | + | |
195 | + return 0; | |
196 | +} | |
197 | + | |
198 | +static int sparse_get_raw_buffer(sparse_header_t *sparse, | |
199 | + chunk_header_t *chunk, | |
200 | + sparse_buffer_t *buffer, | |
201 | + unsigned int blk_sz, | |
202 | + void *data) | |
203 | +{ | |
204 | + unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); | |
205 | + | |
206 | + buffer->type = CHUNK_TYPE_RAW; | |
207 | + buffer->length = chunk_data_sz; | |
208 | + buffer->data = data; | |
209 | + buffer->repeat = 1; | |
210 | + | |
211 | + return 0; | |
212 | +} | |
213 | + | |
214 | +static sparse_buffer_t *sparse_get_data_buffer(sparse_header_t *sparse, | |
215 | + chunk_header_t *chunk, | |
216 | + unsigned int blk_sz, | |
217 | + void **image) | |
218 | +{ | |
219 | + unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk); | |
220 | + sparse_buffer_t *buffer; | |
221 | + void *data = *image; | |
222 | + int ret; | |
223 | + | |
224 | + *image += chunk_data_sz; | |
225 | + | |
226 | + if (!sparse_chunk_has_buffer(chunk)) | |
227 | + return NULL; | |
228 | + | |
229 | + buffer = calloc(sizeof(sparse_buffer_t), 1); | |
230 | + if (!buffer) | |
231 | + return NULL; | |
232 | + | |
233 | + switch (chunk->chunk_type) { | |
234 | + case CHUNK_TYPE_RAW: | |
235 | + ret = sparse_get_raw_buffer(sparse, chunk, buffer, blk_sz, | |
236 | + data); | |
237 | + if (ret) | |
238 | + return NULL; | |
239 | + break; | |
240 | + | |
241 | + case CHUNK_TYPE_FILL: | |
242 | + ret = sparse_get_fill_buffer(sparse, chunk, buffer, blk_sz, | |
243 | + data); | |
244 | + if (ret) | |
245 | + return NULL; | |
246 | + break; | |
247 | + | |
248 | + default: | |
249 | + return NULL; | |
250 | + } | |
251 | + | |
252 | + debug("=== Buffer ===\n"); | |
253 | + debug("length: 0x%x\n", buffer->length); | |
254 | + debug("repeat: 0x%x\n", buffer->repeat); | |
255 | + debug("type: 0x%x\n", buffer->type); | |
256 | + debug("data: 0x%p\n", buffer->data); | |
257 | + | |
258 | + return buffer; | |
259 | +} | |
260 | + | |
261 | +static void sparse_put_data_buffer(sparse_buffer_t *buffer) | |
262 | +{ | |
263 | + if (buffer->type == CHUNK_TYPE_FILL) | |
264 | + free(buffer->data); | |
265 | + | |
266 | + free(buffer); | |
267 | +} | |
268 | + | |
269 | +int store_sparse_image(sparse_storage_t *storage, void *storage_priv, | |
270 | + unsigned int session_id, void *data) | |
271 | +{ | |
272 | + unsigned int chunk, offset; | |
273 | + sparse_header_t *sparse_header; | |
274 | + chunk_header_t *chunk_header; | |
275 | + sparse_buffer_t *buffer; | |
276 | + uint32_t start; | |
277 | + uint32_t total_blocks = 0; | |
278 | + uint32_t skipped = 0; | |
279 | + int i; | |
280 | + | |
281 | + debug("=== Storage ===\n"); | |
282 | + debug("name: %s\n", storage->name); | |
283 | + debug("block_size: 0x%x\n", storage->block_sz); | |
284 | + debug("start: 0x%x\n", storage->start); | |
285 | + debug("size: 0x%x\n", storage->size); | |
286 | + debug("write: 0x%p\n", storage->write); | |
287 | + debug("priv: 0x%p\n", storage_priv); | |
288 | + | |
289 | + sparse_header = sparse_parse_header(&data); | |
290 | + if (!sparse_header) { | |
291 | + printf("sparse header issue\n"); | |
292 | + return -EINVAL; | |
293 | + } | |
294 | + | |
295 | + /* | |
296 | + * Verify that the sparse block size is a multiple of our | |
297 | + * storage backend block size | |
298 | + */ | |
299 | + div_u64_rem(sparse_header->blk_sz, storage->block_sz, &offset); | |
300 | + if (offset) { | |
301 | + printf("%s: Sparse image block size issue [%u]\n", | |
302 | + __func__, sparse_header->blk_sz); | |
303 | + return -EINVAL; | |
304 | + } | |
305 | + | |
306 | + /* | |
307 | + * If it's a new flashing session, start at the beginning of | |
308 | + * the partition. If not, then simply resume where we were. | |
309 | + */ | |
310 | + if (session_id > 0) | |
311 | + start = last_offset; | |
312 | + else | |
313 | + start = storage->start; | |
314 | + | |
315 | + printf("Flashing sparse image on partition %s at offset 0x%x (ID: %d)\n", | |
316 | + storage->name, start * storage->block_sz, session_id); | |
317 | + | |
318 | + /* Start processing chunks */ | |
319 | + for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) { | |
320 | + uint32_t blkcnt; | |
321 | + | |
322 | + chunk_header = sparse_parse_chunk(sparse_header, &data); | |
323 | + if (!chunk_header) { | |
324 | + printf("Unknown chunk type"); | |
325 | + return -EINVAL; | |
326 | + } | |
327 | + | |
328 | + /* | |
329 | + * If we have a DONT_CARE type, just skip the blocks | |
330 | + * and go on parsing the rest of the chunks | |
331 | + */ | |
332 | + if (chunk_header->chunk_type == CHUNK_TYPE_DONT_CARE) { | |
333 | + skipped += sparse_block_size_to_storage(chunk_header->chunk_sz, | |
334 | + storage, | |
335 | + sparse_header); | |
336 | + continue; | |
337 | + } | |
338 | + | |
339 | + /* Retrieve the buffer we're going to write */ | |
340 | + buffer = sparse_get_data_buffer(sparse_header, chunk_header, | |
341 | + storage->block_sz, &data); | |
342 | + if (!buffer) | |
343 | + continue; | |
344 | + | |
345 | + blkcnt = (buffer->length / storage->block_sz) * buffer->repeat; | |
346 | + | |
347 | + if ((start + total_blocks + blkcnt) > | |
348 | + (storage->start + storage->size)) { | |
349 | + printf("%s: Request would exceed partition size!\n", | |
350 | + __func__); | |
351 | + return -EINVAL; | |
352 | + } | |
353 | + | |
354 | + for (i = 0; i < buffer->repeat; i++) { | |
355 | + unsigned long buffer_blk_cnt; | |
356 | + int ret; | |
357 | + | |
358 | + buffer_blk_cnt = buffer->length / storage->block_sz; | |
359 | + | |
360 | + ret = storage->write(storage, storage_priv, | |
361 | + start + total_blocks, | |
362 | + buffer_blk_cnt, | |
363 | + buffer->data); | |
364 | + if (ret < 0) { | |
365 | + printf("%s: Write %d failed %d\n", | |
366 | + __func__, i, ret); | |
367 | + return ret; | |
368 | + } | |
369 | + | |
370 | + total_blocks += ret; | |
371 | + } | |
372 | + | |
373 | + sparse_put_data_buffer(buffer); | |
374 | + } | |
375 | + | |
376 | + debug("Wrote %d blocks, skipped %d, expected to write %d blocks\n", | |
377 | + total_blocks, skipped, | |
378 | + sparse_block_size_to_storage(sparse_header->total_blks, | |
379 | + storage, sparse_header)); | |
380 | + printf("........ wrote %d blocks to '%s'\n", total_blocks, | |
381 | + storage->name); | |
382 | + | |
383 | + if ((total_blocks + skipped) != | |
384 | + sparse_block_size_to_storage(sparse_header->total_blks, | |
385 | + storage, sparse_header)) { | |
386 | + printf("sparse image write failure\n"); | |
387 | + return -EIO; | |
388 | + } | |
389 | + | |
390 | + last_offset = start + total_blocks; | |
391 | + | |
392 | + return 0; | |
393 | +} |
include/aboot.h
1 | -/* | |
2 | - * Copyright 2014 Broadcom Corporation. | |
3 | - * | |
4 | - * SPDX-License-Identifier: GPL-2.0+ | |
5 | - */ | |
6 | - | |
7 | -#include <part.h> | |
8 | -#include <sparse_format.h> | |
9 | - | |
10 | -#define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) | |
11 | - | |
12 | -typedef struct sparse_storage { | |
13 | - unsigned int block_sz; | |
14 | - unsigned int start; | |
15 | - unsigned int size; | |
16 | - const char *name; | |
17 | - | |
18 | - int (*write)(struct sparse_storage *storage, void *priv, | |
19 | - unsigned int offset, unsigned int size, | |
20 | - char *data); | |
21 | -} sparse_storage_t; | |
22 | - | |
23 | -static inline int is_sparse_image(void *buf) | |
24 | -{ | |
25 | - sparse_header_t *s_header = (sparse_header_t *)buf; | |
26 | - | |
27 | - if ((le32_to_cpu(s_header->magic) == SPARSE_HEADER_MAGIC) && | |
28 | - (le16_to_cpu(s_header->major_version) == 1)) | |
29 | - return 1; | |
30 | - | |
31 | - return 0; | |
32 | -} | |
33 | - | |
34 | -int store_sparse_image(sparse_storage_t *storage, void *storage_priv, | |
35 | - unsigned int session_id, void *data); |
include/image-sparse.h
1 | +/* | |
2 | + * Copyright 2014 Broadcom Corporation. | |
3 | + * | |
4 | + * SPDX-License-Identifier: GPL-2.0+ | |
5 | + */ | |
6 | + | |
7 | +#include <part.h> | |
8 | +#include <sparse_format.h> | |
9 | + | |
10 | +#define ROUNDUP(x, y) (((x) + ((y) - 1)) & ~((y) - 1)) | |
11 | + | |
12 | +typedef struct sparse_storage { | |
13 | + unsigned int block_sz; | |
14 | + unsigned int start; | |
15 | + unsigned int size; | |
16 | + const char *name; | |
17 | + | |
18 | + int (*write)(struct sparse_storage *storage, void *priv, | |
19 | + unsigned int offset, unsigned int size, | |
20 | + char *data); | |
21 | +} sparse_storage_t; | |
22 | + | |
23 | +static inline int is_sparse_image(void *buf) | |
24 | +{ | |
25 | + sparse_header_t *s_header = (sparse_header_t *)buf; | |
26 | + | |
27 | + if ((le32_to_cpu(s_header->magic) == SPARSE_HEADER_MAGIC) && | |
28 | + (le16_to_cpu(s_header->major_version) == 1)) | |
29 | + return 1; | |
30 | + | |
31 | + return 0; | |
32 | +} | |
33 | + | |
34 | +int store_sparse_image(sparse_storage_t *storage, void *storage_priv, | |
35 | + unsigned int session_id, void *data); |