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