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