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