blob: 2775254c742e1dbf4fb16d55815dcabcc85afd2f [file] [log] [blame]
Steve Raeb4e4bbe2014-09-03 10:05:51 -07001/*
2 * Copyright (c) 2009, Google Inc.
3 * All rights reserved.
4 *
5 * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
Steve Raee6ca1ad2014-09-03 10:05:54 -07006 * Portions Copyright 2014 Broadcom Corporation.
Steve Raeb4e4bbe2014-09-03 10:05:51 -07007 *
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 *
Steve Rae1c39d852014-09-03 10:05:53 -070032 * 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!
Steve Raeb4e4bbe2014-09-03 10:05:51 -070035 */
36
Steve Raee6ca1ad2014-09-03 10:05:54 -070037#include <config.h>
38#include <common.h>
39#include <aboot.h>
Maxime Ripard40aeeda2015-10-15 14:34:12 +020040#include <div64.h>
Maxime Ripard7bfc3b12015-10-15 14:34:11 +020041#include <errno.h>
Steve Raee6ca1ad2014-09-03 10:05:54 -070042#include <malloc.h>
43#include <part.h>
44#include <sparse_format.h>
45
Maxime Ripard40aeeda2015-10-15 14:34:12 +020046#include <linux/math64.h>
47
Maxime Ripard7bfc3b12015-10-15 14:34:11 +020048typedef struct sparse_buffer {
49 void *data;
50 u32 length;
51 u32 repeat;
52 u16 type;
53} sparse_buffer_t;
54
55static unsigned int sparse_get_chunk_data_size(sparse_header_t *sparse,
56 chunk_header_t *chunk)
57{
58 return chunk->total_sz - sparse->chunk_hdr_sz;
59}
60
61static bool sparse_chunk_has_buffer(chunk_header_t *chunk)
62{
63 switch (chunk->chunk_type) {
64 case CHUNK_TYPE_RAW:
65 case CHUNK_TYPE_FILL:
66 return true;
67
68 default:
69 return false;
70 }
71}
72
Maxime Ripardbb83c0f2015-10-15 14:34:10 +020073static sparse_header_t *sparse_parse_header(void **data)
74{
75 /* Read and skip over sparse image header */
76 sparse_header_t *sparse_header = (sparse_header_t *) *data;
77
78 *data += sparse_header->file_hdr_sz;
79
80 debug("=== Sparse Image Header ===\n");
81 debug("magic: 0x%x\n", sparse_header->magic);
82 debug("major_version: 0x%x\n", sparse_header->major_version);
83 debug("minor_version: 0x%x\n", sparse_header->minor_version);
84 debug("file_hdr_sz: %d\n", sparse_header->file_hdr_sz);
85 debug("chunk_hdr_sz: %d\n", sparse_header->chunk_hdr_sz);
86 debug("blk_sz: %d\n", sparse_header->blk_sz);
87 debug("total_blks: %d\n", sparse_header->total_blks);
88 debug("total_chunks: %d\n", sparse_header->total_chunks);
89
90 return sparse_header;
91}
92
Maxime Ripard7bfc3b12015-10-15 14:34:11 +020093static int sparse_parse_fill_chunk(sparse_header_t *sparse,
94 chunk_header_t *chunk)
95{
96 unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk);
97
98 if (chunk_data_sz != sizeof(uint32_t))
99 return -EINVAL;
100
101 return 0;
102}
103
104static int sparse_parse_raw_chunk(sparse_header_t *sparse,
105 chunk_header_t *chunk)
106{
107 unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk);
108
109 /* Check if the data size is a multiple of the main block size */
110 if (chunk_data_sz % sparse->blk_sz)
111 return -EINVAL;
112
113 /* Check that the chunk size is consistent */
114 if ((chunk_data_sz / sparse->blk_sz) != chunk->chunk_sz)
115 return -EINVAL;
116
117 return 0;
118}
119
120static chunk_header_t *sparse_parse_chunk(sparse_header_t *sparse,
121 void **image)
122{
123 chunk_header_t *chunk = (chunk_header_t *) *image;
124 int ret;
125
126 debug("=== Chunk Header ===\n");
127 debug("chunk_type: 0x%x\n", chunk->chunk_type);
128 debug("chunk_data_sz: 0x%x\n", chunk->chunk_sz);
129 debug("total_size: 0x%x\n", chunk->total_sz);
130
131 switch (chunk->chunk_type) {
132 case CHUNK_TYPE_RAW:
133 ret = sparse_parse_raw_chunk(sparse, chunk);
134 if (ret)
135 return NULL;
136 break;
137
138 case CHUNK_TYPE_FILL:
139 ret = sparse_parse_fill_chunk(sparse, chunk);
140 if (ret)
141 return NULL;
142 break;
143
144 case CHUNK_TYPE_DONT_CARE:
145 case CHUNK_TYPE_CRC32:
146 debug("Ignoring chunk\n");
147 break;
148
149 default:
150 printf("%s: Unknown chunk type: %x\n", __func__,
151 chunk->chunk_type);
152 return NULL;
153 }
154
155 *image += sparse->chunk_hdr_sz;
156
157 return chunk;
158}
159
160static int sparse_get_fill_buffer(sparse_header_t *sparse,
161 chunk_header_t *chunk,
162 sparse_buffer_t *buffer,
163 unsigned int blk_sz,
164 void *data)
165{
166 int i;
167
168 buffer->type = CHUNK_TYPE_FILL;
169
170 /*
171 * We create a buffer of one block, and ask it to be
172 * repeated as many times as needed.
173 */
174 buffer->length = blk_sz;
175 buffer->repeat = (chunk->chunk_sz * sparse->blk_sz) / blk_sz;
176
177 buffer->data = memalign(ARCH_DMA_MINALIGN,
178 ROUNDUP(blk_sz,
179 ARCH_DMA_MINALIGN));
180 if (!buffer->data)
181 return -ENOMEM;
182
183 for (i = 0; i < (buffer->length / sizeof(uint32_t)); i++)
184 ((uint32_t *)buffer->data)[i] = *(uint32_t *)(data);
185
186 return 0;
187}
188
189static int sparse_get_raw_buffer(sparse_header_t *sparse,
190 chunk_header_t *chunk,
191 sparse_buffer_t *buffer,
192 unsigned int blk_sz,
193 void *data)
194{
195 unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk);
196
197 buffer->type = CHUNK_TYPE_RAW;
198 buffer->length = chunk_data_sz;
199 buffer->data = data;
200 buffer->repeat = 1;
201
202 return 0;
203}
204
205static sparse_buffer_t *sparse_get_data_buffer(sparse_header_t *sparse,
206 chunk_header_t *chunk,
207 unsigned int blk_sz,
208 void **image)
209{
210 unsigned int chunk_data_sz = sparse_get_chunk_data_size(sparse, chunk);
211 sparse_buffer_t *buffer;
212 void *data = *image;
213 int ret;
214
215 *image += chunk_data_sz;
216
217 if (!sparse_chunk_has_buffer(chunk))
218 return NULL;
219
220 buffer = calloc(sizeof(sparse_buffer_t), 1);
221 if (!buffer)
222 return NULL;
223
224 switch (chunk->chunk_type) {
225 case CHUNK_TYPE_RAW:
226 ret = sparse_get_raw_buffer(sparse, chunk, buffer, blk_sz,
227 data);
228 if (ret)
229 return NULL;
230 break;
231
232 case CHUNK_TYPE_FILL:
233 ret = sparse_get_fill_buffer(sparse, chunk, buffer, blk_sz,
234 data);
235 if (ret)
236 return NULL;
237 break;
238
239 default:
240 return NULL;
241 }
242
243 debug("=== Buffer ===\n");
244 debug("length: 0x%x\n", buffer->length);
245 debug("repeat: 0x%x\n", buffer->repeat);
246 debug("type: 0x%x\n", buffer->type);
247 debug("data: 0x%p\n", buffer->data);
248
249 return buffer;
250}
251
252static void sparse_put_data_buffer(sparse_buffer_t *buffer)
253{
254 if (buffer->type == CHUNK_TYPE_FILL)
255 free(buffer->data);
256
257 free(buffer);
258}
259
Steve Raee6ca1ad2014-09-03 10:05:54 -0700260void write_sparse_image(block_dev_desc_t *dev_desc,
261 disk_partition_t *info, const char *part_name,
262 void *data, unsigned sz)
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700263{
Maxime Ripard7bfc3b12015-10-15 14:34:11 +0200264 lbaint_t start;
Steve Raee6ca1ad2014-09-03 10:05:54 -0700265 lbaint_t blkcnt;
Maxime Ripard40aeeda2015-10-15 14:34:12 +0200266 unsigned int chunk, offset;
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700267 sparse_header_t *sparse_header;
268 chunk_header_t *chunk_header;
Maxime Ripard7bfc3b12015-10-15 14:34:11 +0200269 sparse_buffer_t *buffer;
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700270 uint32_t total_blocks = 0;
Maxime Ripard7bfc3b12015-10-15 14:34:11 +0200271 uint32_t skipped = 0;
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700272 int i;
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700273
Maxime Ripardbb83c0f2015-10-15 14:34:10 +0200274 sparse_header = sparse_parse_header(&data);
275 if (!sparse_header) {
276 fastboot_fail("sparse header issue\n");
277 return;
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700278 }
279
Maxime Ripard40aeeda2015-10-15 14:34:12 +0200280 /*
281 * Verify that the sparse block size is a multiple of our
282 * storage backend block size
283 */
284 div_u64_rem(sparse_header->blk_sz, info->blksz, &offset);
285 if (offset) {
Steve Raee6ca1ad2014-09-03 10:05:54 -0700286 printf("%s: Sparse image block size issue [%u]\n",
287 __func__, sparse_header->blk_sz);
288 fastboot_fail("sparse image block size issue");
289 return;
290 }
291
292 puts("Flashing Sparse Image\n");
293
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700294 /* Start processing chunks */
Maxime Ripard7bfc3b12015-10-15 14:34:11 +0200295 start = info->start;
296 for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) {
297 chunk_header = sparse_parse_chunk(sparse_header, &data);
298 if (!chunk_header) {
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700299 fastboot_fail("Unknown chunk type");
300 return;
301 }
Maxime Ripard7bfc3b12015-10-15 14:34:11 +0200302
303 /*
304 * If we have a DONT_CARE type, just skip the blocks
305 * and go on parsing the rest of the chunks
306 */
307 if (chunk_header->chunk_type == CHUNK_TYPE_DONT_CARE) {
308 skipped += chunk_header->chunk_sz;
309 continue;
310 }
311
312 /* Retrieve the buffer we're going to write */
313 buffer = sparse_get_data_buffer(sparse_header, chunk_header,
314 info->blksz, &data);
315 if (!buffer)
316 continue;
317
318 blkcnt = (buffer->length / info->blksz) * buffer->repeat;
319
320 if ((start + total_blocks + blkcnt) >
321 (info->start + info->size)) {
322 printf("%s: Request would exceed partition size!\n",
323 __func__);
324 fastboot_fail("Request would exceed partition size!");
325 return;
326 }
327
328 for (i = 0; i < buffer->repeat; i++) {
329 unsigned long buffer_blk_cnt;
330 unsigned long buffer_blks;
331
332 buffer_blk_cnt = buffer->length / info->blksz;
333
334 buffer_blks = dev_desc->block_write(dev_desc->dev,
335 start + total_blocks,
336 buffer_blk_cnt, buffer->data);
337 if (buffer_blks != buffer_blk_cnt) {
338 printf("%s: Write %d failed " LBAFU "\n",
339 __func__, i, buffer_blks);
340 fastboot_fail("flash write failure");
341 return;
342 }
343
344 total_blocks += buffer_blk_cnt;
345 }
346
347 sparse_put_data_buffer(buffer);
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700348 }
349
Maxime Ripard7bfc3b12015-10-15 14:34:11 +0200350 debug("Wrote %d blocks, skipped %d, expected to write %d blocks\n",
351 total_blocks, skipped, sparse_header->total_blks);
352 printf("........ wrote %d blocks to '%s'\n", total_blocks, part_name);
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700353
Steve Rae1c39d852014-09-03 10:05:53 -0700354 if (total_blocks != sparse_header->total_blks)
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700355 fastboot_fail("sparse image write failure");
Steve Raeb4e4bbe2014-09-03 10:05:51 -0700356
357 fastboot_okay("");
358 return;
359}