xref: /rk3399_rockchip-uboot/cmd/tftp_update.c (revision ff06e2a5e708b41585eb4e144fdbd610307fa815)
1 /*
2  * (C) Copyright 2021 Rockchip Electronics Co., Ltd.
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <boot_rkimg.h>
9 #include <crypto.h>
10 #include <dm.h>
11 #include <sysmem.h>
12 #include <u-boot/sha256.h>
13 #ifdef CONFIG_ANDROID_AB
14 #include <android_avb/avb_ops_user.h>
15 #include <android_avb/rk_avb_ops_user.h>
16 #endif
17 #include <asm/arch/vendor.h>
18 
19 DECLARE_GLOBAL_DATA_PTR;
20 
21 #define TFTPUD_I(fmt, args...)	printf("[TFTPUD]: "fmt, ##args)
22 #define TFTPUD_E(fmt, args...)	printf("[TFTPUD-ERROR]: "fmt, ##args)
23 
24 #define UPDATE_HDR_FILE		"update.hdr"
25 #define GPT_ENV_FILE		"gpt_env.txt"
26 #define MAX_UPDATE_HEADER_SIZE	SZ_128K
27 #define MAX_REMAIN_TRIES	3
28 #define SHA256_HASH_SIZE	32
29 
30 struct update_header {
31 	struct list_head images;
32 	void *shared_buf;
33 	u32 version;
34 	u32 rollback_idx;
35 	u32 lba_step;
36 	u32 mb;
37 	int force_update;
38 	const char *spec_partition;
39 };
40 
41 struct local_information {
42 	u32 version;
43 	u32 rollback_idx;
44 	char current_slot[3];
45 };
46 
47 struct image_element {
48 	char file_name[32];
49 	char part_name[32];
50 	void *buf;
51 	u32 size;	/* uint: byte */
52 	u32 lba_start;
53 	u32 lba_offset;
54 	u32 lba_cnt;
55 	u8 remain_tries;
56 	int hash_noffset;
57 	struct list_head node;
58 };
59 
60 static struct update_header update_hdr;
61 static struct local_information local_info;
62 static const char *server_dir;
63 
64 static int tftpfw_version_set(u32 version)
65 {
66 	int ret;
67 
68 	ret = vendor_storage_write(FIRMWARE_VER_ID, &version, sizeof(version));
69 
70 	return ret < 0 ? ret : 0;
71 }
72 
73 static u32 tftpfw_version_get(void)
74 {
75 	u32 version;
76 	int ret;
77 
78 	ret = vendor_storage_read(FIRMWARE_VER_ID, &version, sizeof(version));
79 	if (ret < 0) {
80 		if (ret == -EINVAL) {
81 			version = 0; /* first initial as 0 */
82 			ret = tftpfw_version_set(version);
83 			if (ret < 0)
84 				return ret;
85 		} else {
86 			return ret;
87 		}
88 	}
89 
90 	return version;
91 }
92 
93 static int tftp_download(void *addr, const char *file)
94 {
95 	char tftp_cmd[64];
96 
97 	if (server_dir)
98 		snprintf(tftp_cmd, 64, "tftp 0x%lx %s/%s",
99 				(ulong)addr, server_dir, file);
100 	else
101 		snprintf(tftp_cmd, 64, "tftp 0x%lx %s", (ulong)addr, file);
102 
103 	return run_command(tftp_cmd, 0);
104 }
105 
106 static void update_cleanup(void *fit, struct update_header *hdr)
107 {
108 	struct image_element *e;
109 	struct list_head *node;
110 
111 	list_for_each(node, &hdr->images) {
112 		e = list_entry(node, struct image_element, node);
113 		free(e);
114 	}
115 
116 	if (hdr->shared_buf)
117 		free((phys_addr_t)hdr->shared_buf);
118 	if (fit)
119 		free(fit);
120 }
121 
122 static inline int is_gpt(const char *name)
123 {
124 	if (!name)
125 		return 0;
126 
127 	return !strcmp(name, GPT_ENV_FILE);
128 }
129 
130 static int update_populate_image(void *fit, struct update_header *hdr)
131 {
132 	struct blk_desc *dev_desc;
133 	struct image_element *e;
134 	disk_partition_t part;
135 	const char *name, *dp;
136 	const char *noseq_name;
137 	char *last_part_name = NULL;
138 	uint last_lba_offset = 0;
139 	uint lba_offset;
140 	int images, noffset;
141 	int ret;
142 
143 	images = fdt_path_offset(fit, FIT_IMAGES_PATH);
144 	if (images < 0)
145 		return images;
146 
147 	dev_desc = rockchip_get_bootdev();
148 	if (!dev_desc)
149 		return -ENODEV;
150 
151 	fdt_for_each_subnode(noffset, fit, images) {
152 		name = fit_get_name(fit, noffset, NULL);
153 		printf("# %s:\n", name);
154 
155 		if (is_gpt(name))
156 			continue;
157 
158 		e = malloc(sizeof(*e));
159 		if (!e)
160 			return -ENOMEM;
161 
162 		e->remain_tries = MAX_REMAIN_TRIES;
163 		e->buf = hdr->shared_buf;
164 		e->size = fdtdec_get_uint(fit, noffset, "data-size", -ENODATA);
165 		if (e->size == -ENODATA)
166 			return -ENODATA;
167 
168 		/* part name */
169 		strcpy(e->file_name, name);
170 		strcat(e->file_name, ".part.img");
171 		noseq_name = strstr(name, "-");
172 		if (!noseq_name)
173 			return -EINVAL;
174 		noseq_name++;
175 		dp = strstr(noseq_name, "-");
176 		if (!dp)
177 			return -EINVAL;
178 		dp++;
179 		strlcpy(e->part_name, noseq_name, strlen(noseq_name) - strlen(dp));
180 		ret = part_get_info_by_name_strict(dev_desc, e->part_name, &part);
181 		if (ret < 0) {
182 			TFTPUD_E("No partition '%s'\n", e->part_name);
183 			return -EINVAL;
184 		}
185 
186 		/* lba */
187 		if (!strcmp(last_part_name, e->part_name))
188 			lba_offset = last_lba_offset + hdr->lba_step;
189 		else
190 			lba_offset = 0;
191 
192 		e->lba_start = part.start;
193 		e->lba_offset = lba_offset;
194 		e->lba_cnt = DIV_ROUND_UP(e->size, 512);
195 		e->hash_noffset = fdt_subnode_offset(fit, noffset, "hash");
196 		if (e->hash_noffset < 0)
197 			return e->hash_noffset;
198 
199 		list_add_tail(&e->node, &hdr->images);
200 		last_part_name = e->part_name;
201 		last_lba_offset = lba_offset;
202 
203 		printf("            file: %s\n", e->file_name);
204 		printf("       partition: %s\n", e->part_name);
205 		printf("             buf: 0x%08lx\n", (ulong)e->buf);
206 		printf("            size: 0x%08x\n", e->size);
207 		printf("       lba_start: 0x%08x\n", e->lba_start);
208 		printf("      lba_offset: 0x%08x\n", e->lba_offset);
209 		printf("         lba_cnt: 0x%08x\n", e->lba_cnt);
210 		printf("    remain_tries: %d\n", e->remain_tries);
211 		printf("    hash_noffset: 0x%08x\n\n", e->hash_noffset);
212 	}
213 
214 	return 0;
215 }
216 
217 static void *update_download_hdr(struct update_header *hdr)
218 {
219 	u32 filesz;
220 	void *fit;
221 
222 	fit = memalign(ARCH_DMA_MINALIGN, MAX_UPDATE_HEADER_SIZE);
223 	if (!fit)
224 		return NULL;
225 
226 	if (tftp_download(fit, UPDATE_HDR_FILE)) {
227 		free(fit);
228 		return NULL;
229 	}
230 
231 	if (fdt_check_header(fit)) {
232 		TFTPUD_E("invalid update hdr magic\n");
233 		free(fit);
234 		return NULL;
235 	}
236 
237 	/* sha256 csum was appended at the end of update.hdr */
238 	filesz = env_get_ulong("filesize", 16, 0);
239 	if ((fdt_totalsize(fit) + SHA256_HASH_SIZE) != filesz) {
240 		TFTPUD_E("invalid sha256 hash at the tail of hdr\n");
241 		return NULL;
242 	}
243 
244 	return fit;
245 }
246 
247 #ifndef CONFIG_FIT_SIGNATURE
248 #ifdef CONFIG_DM_CRYPTO
249 static void sha256_checksum(char *input, u32 input_len, u8 *output)
250 {
251 	sha_context csha_ctx;
252 	struct udevice *dev;
253 
254 	dev = crypto_get_device(CRYPTO_SHA256);
255 	if (!dev) {
256 		TFTPUD_E("No crypto device\n");
257 		return;
258 	}
259 	csha_ctx.algo = CRYPTO_SHA256;
260 	csha_ctx.length = input_len;
261 	crypto_sha_csum(dev, &csha_ctx, (char *)input, input_len, output);
262 }
263 #else
264 static void sha256_checksum(char *input, u32 input_len, u8 *output)
265 {
266 	sha256_csum((const uchar *)input, input_len, output);
267 }
268 #endif
269 
270 static int hdr_checksum_verify(void *fit, struct update_header *hdr)
271 {
272 	u8 *hash, csum[SHA256_HASH_SIZE];
273 	int ret, i;
274 
275 	hash = (u8 *)fit + fdt_totalsize(fit);
276 	sha256_checksum(fit, fdt_totalsize(fit), csum);
277 	ret = memcmp(hash, csum, SHA256_HASH_SIZE) ? -EINVAL : 0;
278 	if (ret) {
279 		printf(" update.hash: ");
280 		for (i = 0; i < SHA256_HASH_SIZE; i++)
281 			printf("%02x", hash[i]);
282 		printf("\n");
283 
284 		printf(" calculate hash: ");
285 		for (i = 0; i < SHA256_HASH_SIZE; i++)
286 			printf("%02x", csum[i]);
287 		printf("\n");
288 	}
289 
290 	return ret;
291 }
292 #endif
293 
294 static void print_hdr_local(struct update_header *hdr,
295 			    struct local_information *local)
296 {
297 	printf("# Server:\n");
298 	printf("         version: %d\n", hdr->version);
299 	printf("    rollback_idx: %d\n", hdr->rollback_idx);
300 	printf("    force_update: %d\n", hdr->force_update);
301 	printf("              MB: %d\n", hdr->mb);
302 	printf("        lba_step: 0x%08x\n", hdr->lba_step);
303 	printf("      shared_buf: 0x%08lx - 0x%08lx\n",
304 	       (ulong)hdr->shared_buf, (ulong)hdr->shared_buf + hdr->mb * SZ_1M);
305 	printf("  spec_partition: %s\n\n", hdr->spec_partition);
306 
307 	printf("# Local:\n");
308 	printf("         version: %d\n", local->version);
309 	printf("    rollback_idx: %d\n", local->rollback_idx);
310 	printf("    current_slot: %s\n", local->current_slot);
311 	printf("\n");
312 }
313 
314 static int hdr_param_verify(void *fit, struct update_header *hdr,
315 			    struct local_information *local, int conf)
316 {
317 	u32 size;
318 	int ret;
319 
320 	/* remote */
321 	hdr->version = fdtdec_get_uint(fit, 0, "version", 0);
322 	hdr->rollback_idx = fdtdec_get_uint(fit, conf, "rollback-index", 0);
323 	hdr->force_update = fdtdec_get_uint(fit, conf, "force_update", 0);
324 	hdr->mb = fdtdec_get_uint(fit, conf, "image-size-MB", 0);
325 	size = hdr->mb * SZ_1M;
326 	hdr->lba_step = size / 512;
327 	/* TODO: use sysmem alloc/free */
328 	hdr->shared_buf = malloc(size);
329 	if (!hdr->shared_buf)
330 		return -ENOMEM;
331 
332 	/* local */
333 	ret = tftpfw_version_get();
334 	if (ret < 0) {
335 		TFTPUD_E("Failed to get local firmware version, ret=%d\n", ret);
336 		return local->version;
337 	}
338 	local->version = ret;
339 #ifdef CONFIG_FIT_ROLLBACK_PROTECT
340 	u32 remote_rollback_idx;
341 
342 	ret = fit_rollback_index_verify(fit, FIT_ROLLBACK_INDEX,
343 					&remote_rollback_idx, &local->rollback_idx);
344 	if (ret) {
345 		TFTPUD_E("Failed to get local rollback-index, ret=%d\n", ret);
346 		return ret;
347 	}
348 #else
349 	local->rollback_idx = -1;
350 #endif
351 #ifdef CONFIG_ANDROID_AB
352 	ret = rk_avb_get_current_slot(local->current_slot);
353 	if (ret) {
354 		TFTPUD_E("Failed to get local current slot, ret=%d\n", ret);
355 		return ret;
356 	}
357 #else
358 	strcpy(local->current_slot, "-");
359 #endif
360 
361 	print_hdr_local(hdr, local);
362 
363 	/* verify */
364 	if (hdr->force_update) {
365 		TFTPUD_I("Remote requires force upgrade !\n");
366 		return 0;
367 	}
368 	if (hdr->version < local->version) {
369 		TFTPUD_E("Invalid firmware version: %d(remote) < %d(local)\n",
370 			 hdr->version, local->version);
371 		return -EINVAL;
372 	}
373 #ifdef CONFIG_FIT_ROLLBACK_PROTECT
374 	if (remote_rollback_idx < local->rollback_idx) {
375 		TFTPUD_E("Invalid rollback-index: %d(remote) < %d(local)\n",
376 			 remote_rollback_idx, local->rollback_idx);
377 		return -EINVAL;
378 	}
379 #endif
380 
381 	return 0;
382 }
383 
384 static int update_verify_hdr(void *fit, struct update_header *hdr,
385 			     struct local_information *local)
386 {
387 	const char *name;
388 	int noffset;
389 	int conf;
390 	int ret;
391 
392 	noffset = fdt_path_offset(fit, FIT_CONFS_PATH);
393 	name = fdt_getprop(fit, noffset, "default", NULL);
394 	conf = fdt_subnode_offset(fit, noffset, name);
395 	if (conf < 0)
396 		return conf;
397 
398 #ifdef CONFIG_FIT_SIGNATURE
399 	/* Secure: verify signature */
400 	ret = fit_config_verify(fit, conf);
401 	if (ret)
402 		return ret;
403 
404 	TFTPUD_I("hdr signature verified\n");
405 #else
406 	/* Non-secure: verify hash */
407 	ret = hdr_checksum_verify(fit, hdr);
408 	if (ret)
409 		return ret;
410 
411 	TFTPUD_I("hdr checksum verified\n");
412 #endif
413 	/* verify rollback index ..., etc */
414 	ret = hdr_param_verify(fit, hdr, local, conf);
415 	if (ret)
416 		return ret;
417 
418 	TFTPUD_I("hdr param verified\n");
419 
420 	return 0;
421 }
422 
423 static int update_local_info(void *fit, struct update_header *hdr)
424 {
425 	int ret;
426 
427 	TFTPUD_I("Update local information... ");
428 
429 	ret = tftpfw_version_set(hdr->version);
430 	if (ret) {
431 		TFTPUD_E("Update local param FAIL, ret=%d\n", ret);
432 		return ret;
433 	}
434 	printf("fw_version=%d ", hdr->version);
435 
436 #ifdef CONFIG_FIT_ROLLBACK_PROTECT
437 	ret = fit_write_trusty_rollback_index(hdr->rollback_idx);
438 	if (ret)
439 		return ret;
440 	printf("rollback_idx=%d ", hdr->rollback_idx);
441 #endif
442 	printf("\n");
443 
444 	return 0;
445 }
446 
447 static int update_ignore_image(void *fit, struct update_header *hdr,
448 			       struct image_element *e)
449 {
450 #ifdef CONFIG_ANDROID_AB
451 	char *slot_suffix;
452 
453 	/* Android A/B skip current slot */
454 	slot_suffix = (char *)e->part_name + strlen(e->part_name) - 2;
455 	if (!strcmp(hdr->current_slot, slot_suffix))
456 		return 1;
457 #endif
458 	/* try to find expected target partition */
459 	if (hdr->spec_partition && strcmp(e->part_name, hdr->spec_partition))
460 		return 1;
461 
462 	return 0;
463 }
464 
465 static int download_image(void *fit, struct image_element *e)
466 {
467 	ulong fileaddr;
468 	ulong filesize;
469 	char *msg = "";
470 	int ret;
471 
472 	/* download */
473 	printf("[TFTPUD-0]: download \"%s\" at 0x%lx\n",
474 	       e->file_name, (ulong)e->buf);
475 
476 	ret = tftp_download(e->buf, e->file_name);
477 	if (ret)
478 		return ret;
479 
480 	fileaddr = env_get_ulong("fileaddr", 16, 0);
481 	filesize = env_get_ulong("filesize", 16, 0);
482 	if (!fileaddr || !filesize) {
483 		TFTPUD_E("No fileaddr and filesize\n");
484 		return -ENOENT;
485 	}
486 
487 	if (filesize != e->size) {
488 		TFTPUD_E("Expected filesize 0x%08lx != 0x%08x\n", filesize, e->size);
489 		return -EINVAL;
490 	}
491 
492 	/* verify */
493 	printf("[TFTPUD-1]: verify ");
494 	ret = fit_image_check_hash(fit, e->hash_noffset, e->buf, e->size, &msg);
495 	printf("[%s]\n", ret ? "-" : "+");
496 
497 	return ret;
498 }
499 
500 static int update_flash_image(struct image_element *e)
501 {
502 	struct blk_desc *dev_desc;
503 	int ret;
504 
505 	dev_desc = rockchip_get_bootdev();
506 	if (!dev_desc) {
507 		TFTPUD_E("No boot device\n");
508 		return -ENODEV;
509 	}
510 
511 	printf("[TFTPUD-2]: Flash to \"%s\" partition at LBA offset 0x%08x, "
512 	       "with 0x%08x sectors ... ",
513 	       e->part_name, e->lba_offset, e->lba_cnt);
514 
515 	if (dev_desc->if_type == IF_TYPE_MTD) {
516 		dev_desc->op_flag |= BLK_MTD_CONT_WRITE;
517 		ret = blk_dwrite(dev_desc, e->lba_start + e->lba_offset,
518 				 e->lba_cnt, (void *)e->buf);
519 		dev_desc->op_flag &= ~(BLK_MTD_CONT_WRITE);
520 	} else {
521 		ret = blk_dwrite(dev_desc, e->lba_start + e->lba_offset,
522 				 e->lba_cnt, (void *)e->buf);
523 	}
524 
525 	if (ret != e->lba_cnt)
526 		printf("Failed(%d)\n\n\n", ret);
527 	else
528 		printf("OK\n\n\n");
529 
530 	return 0;
531 }
532 
533 static int update_download_image(void *fit, struct image_element *e)
534 {
535 	int i, ret;
536 
537 	for (i = 0; i < e->remain_tries; i++) {
538 		ret = download_image(fit, e);
539 		if (!ret)
540 			return 0;
541 
542 		TFTPUD_E("retry-%d download\n", i);
543 		continue;
544 	}
545 
546 	return -ENODATA;
547 }
548 
549 static int update_write_gpt(void *fit, struct update_header *hdr)
550 {
551 	struct image_element *e;
552 	char *gpt_parts, *p;
553 	const char *name;
554 	int images;
555 	int noffset;
556 	int ret = 0;
557 
558 	images = fdt_path_offset(fit, FIT_IMAGES_PATH);
559 	if (images < 0)
560 		return images;
561 
562 	noffset = fdt_first_subnode(fit, images);
563 	if (noffset < 0)
564 		return noffset;
565 
566 	/* gpt must be the 1st node */
567 	name = fit_get_name(fit, noffset, NULL);
568 	if (!is_gpt(name))
569 		return 0;
570 
571 	e = malloc(sizeof(*e));
572 	if (!e)
573 		return -ENOMEM;
574 
575 	e->remain_tries = MAX_REMAIN_TRIES;
576 	e->buf = hdr->shared_buf;
577 	e->size = fdtdec_get_uint(fit, noffset, "data-size", -ENODATA);
578 	if (e->size == -ENODATA) {
579 		ret = -EINVAL;
580 		goto out;
581 	}
582 
583 	strcpy(e->file_name, name);
584 	e->hash_noffset = fdt_subnode_offset(fit, noffset, "hash");
585 	if (e->hash_noffset < 0)
586 		return e->hash_noffset;
587 
588 	printf("\n# %s:\n", e->file_name);
589 	printf("             buf: 0x%08lx\n", (ulong)e->buf);
590 	printf("            size: 0x%08x\n", e->size);
591 	printf("    remain_tries: %d\n", e->remain_tries);
592 	printf("    hash_noffset: 0x%08x\n\n", e->hash_noffset);
593 
594 	/* download */
595 	ret = update_download_image(fit, e);
596 	if (ret) {
597 		TFTPUD_E("\"%s\" download fail, ret=%d\n",
598 			 e->file_name, ret);
599 		goto out;
600 	}
601 
602 	/* terminate gpt string */
603 	gpt_parts = (char *)e->buf;
604 	p = gpt_parts + e->size - 1;
605 	*p = '\0';
606 
607 	/* write */
608 	printf("[TFTPUD-2]: Write gpt ...\n");
609 	printf("    %s\n\n", gpt_parts);
610 	env_set("gpt_parts", gpt_parts);
611 	ret = run_command("gpt write ${devtype} ${devnum} ${gpt_parts}", 0);
612 	if (ret) {
613 		printf("Failed to write gpt\n");
614 		ret = -EIO;
615 		goto out;
616 	}
617 	ret = run_command("gpt verify ${devtype} ${devnum} ${gpt_parts}", 0);
618 	if (ret) {
619 		printf("Failed to verify gpt\n");
620 		ret = -EIO;
621 		goto out;
622 	}
623 	printf("\n");
624 out:
625 	free(e);
626 
627 	return ret;
628 }
629 
630 static int do_tftp_update(cmd_tbl_t *cmdtp, int flag,
631 			  int argc, char * const argv[])
632 {
633 	struct local_information *local = &local_info;
634 	struct update_header *hdr = &update_hdr;
635 	struct image_element *e;
636 	struct list_head *node;
637 	const char *dir_part_str;
638 	const char *part_str;
639 	const char *dir_str;
640 	char *dup_str = NULL;
641 	u32 total_success = 0;
642 	u32 total_traverse = 0;
643 	ulong start_ms;
644 	ulong total_ms;
645 	void *fit;
646 	int ret;
647 
648 	start_ms = get_timer(0);
649 	memset(hdr, 0, sizeof(*hdr));
650 	memset(local, 0, sizeof(*local));
651 
652 	/* only handle a single partititon ? */
653 	if (argc > 1) {
654 		dir_part_str = argv[1];
655 		part_str = strchr(dir_part_str, ':');
656 		if (part_str) {
657 			/*
658 			 * eg: tftpupdate image:recovery
659 			 *     tftpupdate image:*
660 			 *     tftpupdate image:
661 			 */
662 			dup_str = strdup(dir_part_str);
663 			dup_str[part_str - dir_part_str] = 0;
664 			dir_str = dup_str;
665 			part_str++;
666 			if (*part_str == '*')
667 				part_str = NULL;
668 		} else {
669 			/* eg: tftpupdate recovery */
670 			dir_str = NULL;
671 			part_str = argv[1];
672 		}
673 	} else {
674 		dir_str = NULL;
675 		part_str = NULL;
676 	}
677 
678 	server_dir = dir_str;
679 	hdr->spec_partition = part_str;
680 	INIT_LIST_HEAD(&hdr->images);
681 
682 	fit = update_download_hdr(hdr);
683 	if (!fit) {
684 		TFTPUD_E("download hdr fail\n");
685 		ret = -EINVAL;
686 		goto out;
687 	}
688 
689 	ret = update_verify_hdr(fit, hdr, local);
690 	if (ret) {
691 		TFTPUD_E("verify hdr fail, ret=%d\n", ret);
692 		goto out;
693 	}
694 
695 	/* flash gpt table early than any other partition */
696 	ret = update_write_gpt(fit, hdr);
697 	if (ret) {
698 		TFTPUD_E("write gpt fail, ret=%d\n", ret);
699 		goto out;
700 	}
701 
702 	ret = update_populate_image(fit, hdr);
703 	if (ret) {
704 		TFTPUD_E("populate image fail, ret=%d\n", ret);
705 		goto out;
706 	}
707 
708 	list_for_each(node, &hdr->images) {
709 		e = list_entry(node, struct image_element, node);
710 		total_traverse++;
711 
712 		/* ignore ? */
713 		if (update_ignore_image(fit, hdr, e))
714 			continue;
715 
716 		ret = update_download_image(fit, e);
717 		if (ret) {
718 			TFTPUD_E("\"%s\" download fail, ret=%d\n",
719 				 e->file_name, ret);
720 			goto out;
721 		}
722 
723 		ret = update_flash_image(e);
724 		if (ret) {
725 			TFTPUD_E("\"%s\" flash fail, ret=%d\n",
726 				 e->file_name, ret);
727 			goto out;
728 		}
729 
730 		total_success++;
731 	}
732 
733 	if (total_success == 0) {
734 		if (hdr->spec_partition) {
735 			TFTPUD_E("No %s partition was found\n", hdr->spec_partition);
736 			ret = CMD_RET_FAILURE;
737 		}
738 		goto out;
739 	}
740 
741 	/* If this is full upgrade, update local info */
742 	if (!hdr->spec_partition)
743 		update_local_info(fit, hdr);
744 out:
745 	update_cleanup(fit, hdr);
746 	if (!ret) {
747 		total_ms = get_timer(start_ms);
748 		TFTPUD_I("tftpupdate is OK (total time: %lds, upgrade: %d/%d), "
749 			 "system reboot is recommend\n",
750 			 total_ms / 1000, total_success, total_traverse);
751 	}
752 
753 	return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
754 }
755 
756 U_BOOT_CMD(
757 	tftpupdate, 2, 1, do_tftp_update,
758 	"Update a set of images organized with FIT via network using TFTP protocol",
759 	"[[server-dir:][partition]"
760 );
761 
762