]> git.ipfire.org Git - people/ms/u-boot.git/blob - common/aboot.c
fastboot: Move fastboot response functions to fastboot core
[people/ms/u-boot.git] / 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 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
61 static 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
73 static 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
93 static 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
104 static 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
120 static 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
160 static 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
189 static 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
205 static 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
252 static 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
260 int write_sparse_image(block_dev_desc_t *dev_desc,
261 disk_partition_t *info, const char *part_name,
262 void *data, unsigned sz)
263 {
264 lbaint_t start;
265 lbaint_t blkcnt;
266 unsigned int chunk, offset;
267 sparse_header_t *sparse_header;
268 chunk_header_t *chunk_header;
269 sparse_buffer_t *buffer;
270 uint32_t total_blocks = 0;
271 uint32_t skipped = 0;
272 int i;
273
274 sparse_header = sparse_parse_header(&data);
275 if (!sparse_header) {
276 printf("sparse header issue\n");
277 return -EINVAL;
278 }
279
280 /*
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) {
286 printf("%s: Sparse image block size issue [%u]\n",
287 __func__, sparse_header->blk_sz);
288 return -EINVAL;
289 }
290
291 puts("Flashing Sparse Image\n");
292
293 /* Start processing chunks */
294 start = info->start;
295 for (chunk = 0; chunk < sparse_header->total_chunks; chunk++) {
296 chunk_header = sparse_parse_chunk(sparse_header, &data);
297 if (!chunk_header) {
298 printf("Unknown chunk type");
299 return -EINVAL;
300 }
301
302 /*
303 * If we have a DONT_CARE type, just skip the blocks
304 * and go on parsing the rest of the chunks
305 */
306 if (chunk_header->chunk_type == CHUNK_TYPE_DONT_CARE) {
307 skipped += chunk_header->chunk_sz;
308 continue;
309 }
310
311 /* Retrieve the buffer we're going to write */
312 buffer = sparse_get_data_buffer(sparse_header, chunk_header,
313 info->blksz, &data);
314 if (!buffer)
315 continue;
316
317 blkcnt = (buffer->length / info->blksz) * buffer->repeat;
318
319 if ((start + total_blocks + blkcnt) >
320 (info->start + info->size)) {
321 printf("%s: Request would exceed partition size!\n",
322 __func__);
323 return -EINVAL;
324 }
325
326 for (i = 0; i < buffer->repeat; i++) {
327 unsigned long buffer_blk_cnt;
328 unsigned long buffer_blks;
329
330 buffer_blk_cnt = buffer->length / info->blksz;
331
332 buffer_blks = dev_desc->block_write(dev_desc->dev,
333 start + total_blocks,
334 buffer_blk_cnt, buffer->data);
335 if (buffer_blks != buffer_blk_cnt) {
336 printf("%s: Write %d failed " LBAFU "\n",
337 __func__, i, buffer_blks);
338 return -EIO;
339 }
340
341 total_blocks += buffer_blk_cnt;
342 }
343
344 sparse_put_data_buffer(buffer);
345 }
346
347 debug("Wrote %d blocks, skipped %d, expected to write %d blocks\n",
348 total_blocks, skipped, sparse_header->total_blks);
349 printf("........ wrote %d blocks to '%s'\n", total_blocks, part_name);
350
351 if (total_blocks != sparse_header->total_blks) {
352 printf("sparse image write failure\n");
353 return -EIO;
354 }
355
356 return 0;
357 }