xref: /openbmc/u-boot/drivers/dfu/dfu.c (revision f6ae1ca0)
1 /*
2  * dfu.c -- DFU back-end routines
3  *
4  * Copyright (C) 2012 Samsung Electronics
5  * author: Lukasz Majewski <l.majewski@samsung.com>
6  *
7  * SPDX-License-Identifier:	GPL-2.0+
8  */
9 
10 #include <common.h>
11 #include <errno.h>
12 #include <malloc.h>
13 #include <mmc.h>
14 #include <fat.h>
15 #include <dfu.h>
16 #include <linux/list.h>
17 #include <linux/compiler.h>
18 
19 static bool dfu_reset_request;
20 static LIST_HEAD(dfu_list);
21 static int dfu_alt_num;
22 static int alt_num_cnt;
23 
24 bool dfu_reset(void)
25 {
26 	return dfu_reset_request;
27 }
28 
29 void dfu_trigger_reset()
30 {
31 	dfu_reset_request = true;
32 }
33 
34 static int dfu_find_alt_num(const char *s)
35 {
36 	int i = 0;
37 
38 	for (; *s; s++)
39 		if (*s == ';')
40 			i++;
41 
42 	return ++i;
43 }
44 
45 int dfu_init_env_entities(char *interface, int dev)
46 {
47 	const char *str_env;
48 	char *env_bkp;
49 	int ret;
50 
51 	str_env = getenv("dfu_alt_info");
52 	if (!str_env) {
53 		error("\"dfu_alt_info\" env variable not defined!\n");
54 		return -EINVAL;
55 	}
56 
57 	env_bkp = strdup(str_env);
58 	ret = dfu_config_entities(env_bkp, interface, dev);
59 	if (ret) {
60 		error("DFU entities configuration failed!\n");
61 		return ret;
62 	}
63 
64 	free(env_bkp);
65 	return 0;
66 }
67 
68 static unsigned char *dfu_buf;
69 static unsigned long dfu_buf_size = CONFIG_SYS_DFU_DATA_BUF_SIZE;
70 
71 unsigned char *dfu_free_buf(void)
72 {
73 	free(dfu_buf);
74 	dfu_buf = NULL;
75 	return dfu_buf;
76 }
77 
78 unsigned long dfu_get_buf_size(void)
79 {
80 	return dfu_buf_size;
81 }
82 
83 unsigned char *dfu_get_buf(void)
84 {
85 	char *s;
86 
87 	if (dfu_buf != NULL)
88 		return dfu_buf;
89 
90 	s = getenv("dfu_bufsiz");
91 	dfu_buf_size = s ? (unsigned long)simple_strtol(s, NULL, 16) :
92 			CONFIG_SYS_DFU_DATA_BUF_SIZE;
93 
94 	dfu_buf = memalign(CONFIG_SYS_CACHELINE_SIZE, dfu_buf_size);
95 	if (dfu_buf == NULL)
96 		printf("%s: Could not memalign 0x%lx bytes\n",
97 		       __func__, dfu_buf_size);
98 
99 	return dfu_buf;
100 }
101 
102 static int dfu_write_buffer_drain(struct dfu_entity *dfu)
103 {
104 	long w_size;
105 	int ret;
106 
107 	/* flush size? */
108 	w_size = dfu->i_buf - dfu->i_buf_start;
109 	if (w_size == 0)
110 		return 0;
111 
112 	/* update CRC32 */
113 	dfu->crc = crc32(dfu->crc, dfu->i_buf_start, w_size);
114 
115 	ret = dfu->write_medium(dfu, dfu->offset, dfu->i_buf_start, &w_size);
116 	if (ret)
117 		debug("%s: Write error!\n", __func__);
118 
119 	/* point back */
120 	dfu->i_buf = dfu->i_buf_start;
121 
122 	/* update offset */
123 	dfu->offset += w_size;
124 
125 	puts("#");
126 
127 	return ret;
128 }
129 
130 int dfu_flush(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
131 {
132 	int ret = 0;
133 
134 	if (dfu->flush_medium)
135 		ret = dfu->flush_medium(dfu);
136 
137 	printf("\nDFU complete CRC32: 0x%08x\n", dfu->crc);
138 
139 	/* clear everything */
140 	dfu_free_buf();
141 	dfu->crc = 0;
142 	dfu->offset = 0;
143 	dfu->i_blk_seq_num = 0;
144 	dfu->i_buf_start = dfu_buf;
145 	dfu->i_buf_end = dfu_buf;
146 	dfu->i_buf = dfu->i_buf_start;
147 	dfu->inited = 0;
148 
149 	return ret;
150 }
151 
152 int dfu_write(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
153 {
154 	int ret = 0;
155 	int tret;
156 
157 	debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x offset: 0x%llx bufoffset: 0x%x\n",
158 	      __func__, dfu->name, buf, size, blk_seq_num, dfu->offset,
159 	      dfu->i_buf - dfu->i_buf_start);
160 
161 	if (!dfu->inited) {
162 		/* initial state */
163 		dfu->crc = 0;
164 		dfu->offset = 0;
165 		dfu->bad_skip = 0;
166 		dfu->i_blk_seq_num = 0;
167 		dfu->i_buf_start = dfu_get_buf();
168 		if (dfu->i_buf_start == NULL)
169 			return -ENOMEM;
170 		dfu->i_buf_end = dfu_get_buf() + dfu_buf_size;
171 		dfu->i_buf = dfu->i_buf_start;
172 
173 		dfu->inited = 1;
174 	}
175 
176 	if (dfu->i_blk_seq_num != blk_seq_num) {
177 		printf("%s: Wrong sequence number! [%d] [%d]\n",
178 		       __func__, dfu->i_blk_seq_num, blk_seq_num);
179 		return -1;
180 	}
181 
182 	/* DFU 1.1 standard says:
183 	 * The wBlockNum field is a block sequence number. It increments each
184 	 * time a block is transferred, wrapping to zero from 65,535. It is used
185 	 * to provide useful context to the DFU loader in the device."
186 	 *
187 	 * This means that it's a 16 bit counter that roll-overs at
188 	 * 0xffff -> 0x0000. By having a typical 4K transfer block
189 	 * we roll-over at exactly 256MB. Not very fun to debug.
190 	 *
191 	 * Handling rollover, and having an inited variable,
192 	 * makes things work.
193 	 */
194 
195 	/* handle rollover */
196 	dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
197 
198 	/* flush buffer if overflow */
199 	if ((dfu->i_buf + size) > dfu->i_buf_end) {
200 		tret = dfu_write_buffer_drain(dfu);
201 		if (ret == 0)
202 			ret = tret;
203 	}
204 
205 	/* we should be in buffer now (if not then size too large) */
206 	if ((dfu->i_buf + size) > dfu->i_buf_end) {
207 		error("Buffer overflow! (0x%p + 0x%x > 0x%p)\n", dfu->i_buf,
208 		      size, dfu->i_buf_end);
209 		return -1;
210 	}
211 
212 	memcpy(dfu->i_buf, buf, size);
213 	dfu->i_buf += size;
214 
215 	/* if end or if buffer full flush */
216 	if (size == 0 || (dfu->i_buf + size) > dfu->i_buf_end) {
217 		tret = dfu_write_buffer_drain(dfu);
218 		if (ret == 0)
219 			ret = tret;
220 	}
221 
222 	return ret = 0 ? size : ret;
223 }
224 
225 static int dfu_read_buffer_fill(struct dfu_entity *dfu, void *buf, int size)
226 {
227 	long chunk;
228 	int ret, readn;
229 
230 	readn = 0;
231 	while (size > 0) {
232 		/* get chunk that can be read */
233 		chunk = min(size, dfu->b_left);
234 		/* consume */
235 		if (chunk > 0) {
236 			memcpy(buf, dfu->i_buf, chunk);
237 			dfu->crc = crc32(dfu->crc, buf, chunk);
238 			dfu->i_buf += chunk;
239 			dfu->b_left -= chunk;
240 			dfu->r_left -= chunk;
241 			size -= chunk;
242 			buf += chunk;
243 			readn += chunk;
244 		}
245 
246 		/* all done */
247 		if (size > 0) {
248 			/* no more to read */
249 			if (dfu->r_left == 0)
250 				break;
251 
252 			dfu->i_buf = dfu->i_buf_start;
253 			dfu->b_left = dfu->i_buf_end - dfu->i_buf_start;
254 
255 			/* got to read, but buffer is empty */
256 			if (dfu->b_left > dfu->r_left)
257 				dfu->b_left = dfu->r_left;
258 			ret = dfu->read_medium(dfu, dfu->offset, dfu->i_buf,
259 					&dfu->b_left);
260 			if (ret != 0) {
261 				debug("%s: Read error!\n", __func__);
262 				return ret;
263 			}
264 			dfu->offset += dfu->b_left;
265 			dfu->r_left -= dfu->b_left;
266 
267 			puts("#");
268 		}
269 	}
270 
271 	return readn;
272 }
273 
274 int dfu_read(struct dfu_entity *dfu, void *buf, int size, int blk_seq_num)
275 {
276 	int ret = 0;
277 
278 	debug("%s: name: %s buf: 0x%p size: 0x%x p_num: 0x%x i_buf: 0x%p\n",
279 	       __func__, dfu->name, buf, size, blk_seq_num, dfu->i_buf);
280 
281 	if (!dfu->inited) {
282 		dfu->i_buf_start = dfu_get_buf();
283 		if (dfu->i_buf_start == NULL)
284 			return -ENOMEM;
285 
286 		ret = dfu->read_medium(dfu, 0, dfu->i_buf_start, &dfu->r_left);
287 		if (ret != 0) {
288 			debug("%s: failed to get r_left\n", __func__);
289 			return ret;
290 		}
291 
292 		debug("%s: %s %ld [B]\n", __func__, dfu->name, dfu->r_left);
293 
294 		dfu->i_blk_seq_num = 0;
295 		dfu->crc = 0;
296 		dfu->offset = 0;
297 		dfu->i_buf_end = dfu_get_buf() + dfu_buf_size;
298 		dfu->i_buf = dfu->i_buf_start;
299 		dfu->b_left = min(dfu_buf_size, dfu->r_left);
300 
301 		dfu->bad_skip = 0;
302 
303 		dfu->inited = 1;
304 	}
305 
306 	if (dfu->i_blk_seq_num != blk_seq_num) {
307 		printf("%s: Wrong sequence number! [%d] [%d]\n",
308 		       __func__, dfu->i_blk_seq_num, blk_seq_num);
309 		return -1;
310 	}
311 	/* handle rollover */
312 	dfu->i_blk_seq_num = (dfu->i_blk_seq_num + 1) & 0xffff;
313 
314 	ret = dfu_read_buffer_fill(dfu, buf, size);
315 	if (ret < 0) {
316 		printf("%s: Failed to fill buffer\n", __func__);
317 		return -1;
318 	}
319 
320 	if (ret < size) {
321 		debug("%s: %s CRC32: 0x%x\n", __func__, dfu->name, dfu->crc);
322 		puts("\nUPLOAD ... done\nCtrl+C to exit ...\n");
323 
324 		dfu_free_buf();
325 		dfu->i_blk_seq_num = 0;
326 		dfu->crc = 0;
327 		dfu->offset = 0;
328 		dfu->i_buf_start = dfu_buf;
329 		dfu->i_buf_end = dfu_buf;
330 		dfu->i_buf = dfu->i_buf_start;
331 		dfu->b_left = 0;
332 
333 		dfu->bad_skip = 0;
334 
335 		dfu->inited = 0;
336 	}
337 
338 	return ret;
339 }
340 
341 static int dfu_fill_entity(struct dfu_entity *dfu, char *s, int alt,
342 			   char *interface, int num)
343 {
344 	char *st;
345 
346 	debug("%s: %s interface: %s num: %d\n", __func__, s, interface, num);
347 	st = strsep(&s, " ");
348 	strcpy(dfu->name, st);
349 
350 	dfu->dev_num = num;
351 	dfu->alt = alt;
352 
353 	/* Specific for mmc device */
354 	if (strcmp(interface, "mmc") == 0) {
355 		if (dfu_fill_entity_mmc(dfu, s))
356 			return -1;
357 	} else if (strcmp(interface, "nand") == 0) {
358 		if (dfu_fill_entity_nand(dfu, s))
359 			return -1;
360 	} else if (strcmp(interface, "ram") == 0) {
361 		if (dfu_fill_entity_ram(dfu, s))
362 			return -1;
363 	} else {
364 		printf("%s: Device %s not (yet) supported!\n",
365 		       __func__,  interface);
366 		return -1;
367 	}
368 
369 	return 0;
370 }
371 
372 void dfu_free_entities(void)
373 {
374 	struct dfu_entity *dfu, *p, *t = NULL;
375 
376 	list_for_each_entry_safe_reverse(dfu, p, &dfu_list, list) {
377 		list_del(&dfu->list);
378 		t = dfu;
379 	}
380 	if (t)
381 		free(t);
382 	INIT_LIST_HEAD(&dfu_list);
383 
384 	alt_num_cnt = 0;
385 }
386 
387 int dfu_config_entities(char *env, char *interface, int num)
388 {
389 	struct dfu_entity *dfu;
390 	int i, ret;
391 	char *s;
392 
393 	dfu_alt_num = dfu_find_alt_num(env);
394 	debug("%s: dfu_alt_num=%d\n", __func__, dfu_alt_num);
395 
396 	dfu = calloc(sizeof(*dfu), dfu_alt_num);
397 	if (!dfu)
398 		return -1;
399 	for (i = 0; i < dfu_alt_num; i++) {
400 
401 		s = strsep(&env, ";");
402 		ret = dfu_fill_entity(&dfu[i], s, alt_num_cnt, interface, num);
403 		if (ret)
404 			return -1;
405 
406 		list_add_tail(&dfu[i].list, &dfu_list);
407 		alt_num_cnt++;
408 	}
409 
410 	return 0;
411 }
412 
413 const char *dfu_get_dev_type(enum dfu_device_type t)
414 {
415 	const char *dev_t[] = {NULL, "eMMC", "OneNAND", "NAND", "RAM" };
416 	return dev_t[t];
417 }
418 
419 const char *dfu_get_layout(enum dfu_layout l)
420 {
421 	const char *dfu_layout[] = {NULL, "RAW_ADDR", "FAT", "EXT2",
422 					   "EXT3", "EXT4", "RAM_ADDR" };
423 	return dfu_layout[l];
424 }
425 
426 void dfu_show_entities(void)
427 {
428 	struct dfu_entity *dfu;
429 
430 	puts("DFU alt settings list:\n");
431 
432 	list_for_each_entry(dfu, &dfu_list, list) {
433 		printf("dev: %s alt: %d name: %s layout: %s\n",
434 		       dfu_get_dev_type(dfu->dev_type), dfu->alt,
435 		       dfu->name, dfu_get_layout(dfu->layout));
436 	}
437 }
438 
439 int dfu_get_alt_number(void)
440 {
441 	return dfu_alt_num;
442 }
443 
444 struct dfu_entity *dfu_get_entity(int alt)
445 {
446 	struct dfu_entity *dfu;
447 
448 	list_for_each_entry(dfu, &dfu_list, list) {
449 		if (dfu->alt == alt)
450 			return dfu;
451 	}
452 
453 	return NULL;
454 }
455 
456 int dfu_get_alt(char *name)
457 {
458 	struct dfu_entity *dfu;
459 
460 	list_for_each_entry(dfu, &dfu_list, list) {
461 		if (!strncmp(dfu->name, name, strlen(dfu->name)))
462 			return dfu->alt;
463 	}
464 
465 	return -ENODEV;
466 }
467