12fb449a6SJoseph Chen /* 22fb449a6SJoseph Chen * (C) Copyright 2021 Rockchip Electronics Co., Ltd. 32fb449a6SJoseph Chen * 42fb449a6SJoseph Chen * SPDX-License-Identifier: GPL-2.0+ 52fb449a6SJoseph Chen */ 62fb449a6SJoseph Chen 72fb449a6SJoseph Chen #include <common.h> 82fb449a6SJoseph Chen #include <boot_rkimg.h> 92fb449a6SJoseph Chen #include <crypto.h> 102fb449a6SJoseph Chen #include <dm.h> 112fb449a6SJoseph Chen #include <sysmem.h> 122fb449a6SJoseph Chen #include <u-boot/sha256.h> 132fb449a6SJoseph Chen #ifdef CONFIG_ANDROID_AB 142fb449a6SJoseph Chen #include <android_avb/avb_ops_user.h> 152fb449a6SJoseph Chen #include <android_avb/rk_avb_ops_user.h> 162fb449a6SJoseph Chen #endif 172fb449a6SJoseph Chen #include <asm/arch/vendor.h> 182fb449a6SJoseph Chen 192fb449a6SJoseph Chen DECLARE_GLOBAL_DATA_PTR; 202fb449a6SJoseph Chen 212fb449a6SJoseph Chen #define TFTPUD_I(fmt, args...) printf("[TFTPUD]: "fmt, ##args) 222fb449a6SJoseph Chen #define TFTPUD_E(fmt, args...) printf("[TFTPUD-ERROR]: "fmt, ##args) 232fb449a6SJoseph Chen 242fb449a6SJoseph Chen #define UPDATE_HDR_FILE "update.hdr" 252fb449a6SJoseph Chen #define GPT_ENV_FILE "gpt_env.txt" 262fb449a6SJoseph Chen #define MAX_UPDATE_HEADER_SIZE SZ_128K 272fb449a6SJoseph Chen #define MAX_REMAIN_TRIES 3 282fb449a6SJoseph Chen #define SHA256_HASH_SIZE 32 292fb449a6SJoseph Chen 302fb449a6SJoseph Chen struct update_header { 312fb449a6SJoseph Chen struct list_head images; 322fb449a6SJoseph Chen void *shared_buf; 332fb449a6SJoseph Chen u32 version; 342fb449a6SJoseph Chen u32 rollback_idx; 352fb449a6SJoseph Chen u32 lba_step; 362fb449a6SJoseph Chen u32 mb; 372fb449a6SJoseph Chen int force_update; 382fb449a6SJoseph Chen const char *spec_partition; 392fb449a6SJoseph Chen }; 402fb449a6SJoseph Chen 412fb449a6SJoseph Chen struct local_information { 422fb449a6SJoseph Chen u32 version; 432fb449a6SJoseph Chen u32 rollback_idx; 442fb449a6SJoseph Chen char current_slot[3]; 452fb449a6SJoseph Chen }; 462fb449a6SJoseph Chen 472fb449a6SJoseph Chen struct image_element { 482fb449a6SJoseph Chen char file_name[32]; 492fb449a6SJoseph Chen char part_name[32]; 502fb449a6SJoseph Chen void *buf; 512fb449a6SJoseph Chen u32 size; /* uint: byte */ 522fb449a6SJoseph Chen u32 lba_start; 532fb449a6SJoseph Chen u32 lba_offset; 542fb449a6SJoseph Chen u32 lba_cnt; 552fb449a6SJoseph Chen u8 remain_tries; 562fb449a6SJoseph Chen int hash_noffset; 572fb449a6SJoseph Chen struct list_head node; 582fb449a6SJoseph Chen }; 592fb449a6SJoseph Chen 602fb449a6SJoseph Chen static struct update_header update_hdr; 612fb449a6SJoseph Chen static struct local_information local_info; 622fb449a6SJoseph Chen static const char *server_dir; 632fb449a6SJoseph Chen 642fb449a6SJoseph Chen static int tftpfw_version_set(u32 version) 652fb449a6SJoseph Chen { 662fb449a6SJoseph Chen int ret; 672fb449a6SJoseph Chen 682fb449a6SJoseph Chen ret = vendor_storage_write(FIRMWARE_VER_ID, &version, sizeof(version)); 692fb449a6SJoseph Chen 702fb449a6SJoseph Chen return ret < 0 ? ret : 0; 712fb449a6SJoseph Chen } 722fb449a6SJoseph Chen 732fb449a6SJoseph Chen static u32 tftpfw_version_get(void) 742fb449a6SJoseph Chen { 752fb449a6SJoseph Chen u32 version; 762fb449a6SJoseph Chen int ret; 772fb449a6SJoseph Chen 782fb449a6SJoseph Chen ret = vendor_storage_read(FIRMWARE_VER_ID, &version, sizeof(version)); 792fb449a6SJoseph Chen if (ret < 0) { 802fb449a6SJoseph Chen if (ret == -EINVAL) { 812fb449a6SJoseph Chen version = 0; /* first initial as 0 */ 82*892e9cd5SJoseph Chen TFTPUD_I("Initial firmware version as 0\n"); 832fb449a6SJoseph Chen ret = tftpfw_version_set(version); 842fb449a6SJoseph Chen if (ret < 0) 852fb449a6SJoseph Chen return ret; 862fb449a6SJoseph Chen } else { 872fb449a6SJoseph Chen return ret; 882fb449a6SJoseph Chen } 892fb449a6SJoseph Chen } 902fb449a6SJoseph Chen 912fb449a6SJoseph Chen return version; 922fb449a6SJoseph Chen } 932fb449a6SJoseph Chen 942fb449a6SJoseph Chen static int tftp_download(void *addr, const char *file) 952fb449a6SJoseph Chen { 962fb449a6SJoseph Chen char tftp_cmd[64]; 972fb449a6SJoseph Chen 982fb449a6SJoseph Chen if (server_dir) 992fb449a6SJoseph Chen snprintf(tftp_cmd, 64, "tftp 0x%lx %s/%s", 1002fb449a6SJoseph Chen (ulong)addr, server_dir, file); 1012fb449a6SJoseph Chen else 1022fb449a6SJoseph Chen snprintf(tftp_cmd, 64, "tftp 0x%lx %s", (ulong)addr, file); 1032fb449a6SJoseph Chen 1042fb449a6SJoseph Chen return run_command(tftp_cmd, 0); 1052fb449a6SJoseph Chen } 1062fb449a6SJoseph Chen 1072fb449a6SJoseph Chen static void update_cleanup(void *fit, struct update_header *hdr) 1082fb449a6SJoseph Chen { 1092fb449a6SJoseph Chen struct image_element *e; 1102fb449a6SJoseph Chen struct list_head *node; 1112fb449a6SJoseph Chen 1122fb449a6SJoseph Chen list_for_each(node, &hdr->images) { 1132fb449a6SJoseph Chen e = list_entry(node, struct image_element, node); 1142fb449a6SJoseph Chen free(e); 1152fb449a6SJoseph Chen } 1162fb449a6SJoseph Chen 1172fb449a6SJoseph Chen if (hdr->shared_buf) 1182fb449a6SJoseph Chen free((phys_addr_t)hdr->shared_buf); 1192fb449a6SJoseph Chen if (fit) 1202fb449a6SJoseph Chen free(fit); 1212fb449a6SJoseph Chen } 1222fb449a6SJoseph Chen 1232fb449a6SJoseph Chen static inline int is_gpt(const char *name) 1242fb449a6SJoseph Chen { 1252fb449a6SJoseph Chen if (!name) 1262fb449a6SJoseph Chen return 0; 1272fb449a6SJoseph Chen 1282fb449a6SJoseph Chen return !strcmp(name, GPT_ENV_FILE); 1292fb449a6SJoseph Chen } 1302fb449a6SJoseph Chen 1312fb449a6SJoseph Chen static int update_populate_image(void *fit, struct update_header *hdr) 1322fb449a6SJoseph Chen { 1332fb449a6SJoseph Chen struct blk_desc *dev_desc; 1342fb449a6SJoseph Chen struct image_element *e; 1352fb449a6SJoseph Chen disk_partition_t part; 1362fb449a6SJoseph Chen const char *name, *dp; 1372fb449a6SJoseph Chen const char *noseq_name; 1382fb449a6SJoseph Chen char *last_part_name = NULL; 1392fb449a6SJoseph Chen uint last_lba_offset = 0; 1402fb449a6SJoseph Chen uint lba_offset; 1412fb449a6SJoseph Chen int images, noffset; 1422fb449a6SJoseph Chen int ret; 1432fb449a6SJoseph Chen 1442fb449a6SJoseph Chen images = fdt_path_offset(fit, FIT_IMAGES_PATH); 1452fb449a6SJoseph Chen if (images < 0) 1462fb449a6SJoseph Chen return images; 1472fb449a6SJoseph Chen 1482fb449a6SJoseph Chen dev_desc = rockchip_get_bootdev(); 1492fb449a6SJoseph Chen if (!dev_desc) 1502fb449a6SJoseph Chen return -ENODEV; 1512fb449a6SJoseph Chen 1522fb449a6SJoseph Chen fdt_for_each_subnode(noffset, fit, images) { 1532fb449a6SJoseph Chen name = fit_get_name(fit, noffset, NULL); 1542fb449a6SJoseph Chen printf("# %s:\n", name); 1552fb449a6SJoseph Chen 1562fb449a6SJoseph Chen if (is_gpt(name)) 1572fb449a6SJoseph Chen continue; 1582fb449a6SJoseph Chen 1592fb449a6SJoseph Chen e = malloc(sizeof(*e)); 1602fb449a6SJoseph Chen if (!e) 1612fb449a6SJoseph Chen return -ENOMEM; 1622fb449a6SJoseph Chen 1632fb449a6SJoseph Chen e->remain_tries = MAX_REMAIN_TRIES; 1642fb449a6SJoseph Chen e->buf = hdr->shared_buf; 1652fb449a6SJoseph Chen e->size = fdtdec_get_uint(fit, noffset, "data-size", -ENODATA); 1662fb449a6SJoseph Chen if (e->size == -ENODATA) 1672fb449a6SJoseph Chen return -ENODATA; 1682fb449a6SJoseph Chen 1692fb449a6SJoseph Chen /* part name */ 1702fb449a6SJoseph Chen strcpy(e->file_name, name); 1712fb449a6SJoseph Chen strcat(e->file_name, ".part.img"); 1722fb449a6SJoseph Chen noseq_name = strstr(name, "-"); 1732fb449a6SJoseph Chen if (!noseq_name) 1742fb449a6SJoseph Chen return -EINVAL; 1752fb449a6SJoseph Chen noseq_name++; 1762fb449a6SJoseph Chen dp = strstr(noseq_name, "-"); 1772fb449a6SJoseph Chen if (!dp) 1782fb449a6SJoseph Chen return -EINVAL; 1792fb449a6SJoseph Chen dp++; 1802fb449a6SJoseph Chen strlcpy(e->part_name, noseq_name, strlen(noseq_name) - strlen(dp)); 1812fb449a6SJoseph Chen ret = part_get_info_by_name_strict(dev_desc, e->part_name, &part); 1822fb449a6SJoseph Chen if (ret < 0) { 1832fb449a6SJoseph Chen TFTPUD_E("No partition '%s'\n", e->part_name); 1842fb449a6SJoseph Chen return -EINVAL; 1852fb449a6SJoseph Chen } 1862fb449a6SJoseph Chen 1872fb449a6SJoseph Chen /* lba */ 1882fb449a6SJoseph Chen if (!strcmp(last_part_name, e->part_name)) 1892fb449a6SJoseph Chen lba_offset = last_lba_offset + hdr->lba_step; 1902fb449a6SJoseph Chen else 1912fb449a6SJoseph Chen lba_offset = 0; 1922fb449a6SJoseph Chen 1932fb449a6SJoseph Chen e->lba_start = part.start; 1942fb449a6SJoseph Chen e->lba_offset = lba_offset; 1952fb449a6SJoseph Chen e->lba_cnt = DIV_ROUND_UP(e->size, 512); 1962fb449a6SJoseph Chen e->hash_noffset = fdt_subnode_offset(fit, noffset, "hash"); 1972fb449a6SJoseph Chen if (e->hash_noffset < 0) 1982fb449a6SJoseph Chen return e->hash_noffset; 1992fb449a6SJoseph Chen 2002fb449a6SJoseph Chen list_add_tail(&e->node, &hdr->images); 2012fb449a6SJoseph Chen last_part_name = e->part_name; 2022fb449a6SJoseph Chen last_lba_offset = lba_offset; 2032fb449a6SJoseph Chen 2042fb449a6SJoseph Chen printf(" file: %s\n", e->file_name); 2052fb449a6SJoseph Chen printf(" partition: %s\n", e->part_name); 2062fb449a6SJoseph Chen printf(" buf: 0x%08lx\n", (ulong)e->buf); 2072fb449a6SJoseph Chen printf(" size: 0x%08x\n", e->size); 2082fb449a6SJoseph Chen printf(" lba_start: 0x%08x\n", e->lba_start); 2092fb449a6SJoseph Chen printf(" lba_offset: 0x%08x\n", e->lba_offset); 2102fb449a6SJoseph Chen printf(" lba_cnt: 0x%08x\n", e->lba_cnt); 2112fb449a6SJoseph Chen printf(" remain_tries: %d\n", e->remain_tries); 2122fb449a6SJoseph Chen printf(" hash_noffset: 0x%08x\n\n", e->hash_noffset); 2132fb449a6SJoseph Chen } 2142fb449a6SJoseph Chen 2152fb449a6SJoseph Chen return 0; 2162fb449a6SJoseph Chen } 2172fb449a6SJoseph Chen 2182fb449a6SJoseph Chen static void *update_download_hdr(struct update_header *hdr) 2192fb449a6SJoseph Chen { 2202fb449a6SJoseph Chen u32 filesz; 2212fb449a6SJoseph Chen void *fit; 2222fb449a6SJoseph Chen 2232fb449a6SJoseph Chen fit = memalign(ARCH_DMA_MINALIGN, MAX_UPDATE_HEADER_SIZE); 2242fb449a6SJoseph Chen if (!fit) 2252fb449a6SJoseph Chen return NULL; 2262fb449a6SJoseph Chen 2272fb449a6SJoseph Chen if (tftp_download(fit, UPDATE_HDR_FILE)) { 2282fb449a6SJoseph Chen free(fit); 2292fb449a6SJoseph Chen return NULL; 2302fb449a6SJoseph Chen } 2312fb449a6SJoseph Chen 2322fb449a6SJoseph Chen if (fdt_check_header(fit)) { 2332fb449a6SJoseph Chen TFTPUD_E("invalid update hdr magic\n"); 2342fb449a6SJoseph Chen free(fit); 2352fb449a6SJoseph Chen return NULL; 2362fb449a6SJoseph Chen } 2372fb449a6SJoseph Chen 2382fb449a6SJoseph Chen /* sha256 csum was appended at the end of update.hdr */ 2392fb449a6SJoseph Chen filesz = env_get_ulong("filesize", 16, 0); 2402fb449a6SJoseph Chen if ((fdt_totalsize(fit) + SHA256_HASH_SIZE) != filesz) { 2412fb449a6SJoseph Chen TFTPUD_E("invalid sha256 hash at the tail of hdr\n"); 2422fb449a6SJoseph Chen return NULL; 2432fb449a6SJoseph Chen } 2442fb449a6SJoseph Chen 2452fb449a6SJoseph Chen return fit; 2462fb449a6SJoseph Chen } 2472fb449a6SJoseph Chen 2482fb449a6SJoseph Chen #ifndef CONFIG_FIT_SIGNATURE 2492fb449a6SJoseph Chen #ifdef CONFIG_DM_CRYPTO 2502fb449a6SJoseph Chen static void sha256_checksum(char *input, u32 input_len, u8 *output) 2512fb449a6SJoseph Chen { 2522fb449a6SJoseph Chen sha_context csha_ctx; 2532fb449a6SJoseph Chen struct udevice *dev; 2542fb449a6SJoseph Chen 2552fb449a6SJoseph Chen dev = crypto_get_device(CRYPTO_SHA256); 2562fb449a6SJoseph Chen if (!dev) { 2572fb449a6SJoseph Chen TFTPUD_E("No crypto device\n"); 2582fb449a6SJoseph Chen return; 2592fb449a6SJoseph Chen } 2602fb449a6SJoseph Chen csha_ctx.algo = CRYPTO_SHA256; 2612fb449a6SJoseph Chen csha_ctx.length = input_len; 2622fb449a6SJoseph Chen crypto_sha_csum(dev, &csha_ctx, (char *)input, input_len, output); 2632fb449a6SJoseph Chen } 2642fb449a6SJoseph Chen #else 2652fb449a6SJoseph Chen static void sha256_checksum(char *input, u32 input_len, u8 *output) 2662fb449a6SJoseph Chen { 2672fb449a6SJoseph Chen sha256_csum((const uchar *)input, input_len, output); 2682fb449a6SJoseph Chen } 2692fb449a6SJoseph Chen #endif 2702fb449a6SJoseph Chen 2712fb449a6SJoseph Chen static int hdr_checksum_verify(void *fit, struct update_header *hdr) 2722fb449a6SJoseph Chen { 2732fb449a6SJoseph Chen u8 *hash, csum[SHA256_HASH_SIZE]; 2742fb449a6SJoseph Chen int ret, i; 2752fb449a6SJoseph Chen 2762fb449a6SJoseph Chen hash = (u8 *)fit + fdt_totalsize(fit); 2772fb449a6SJoseph Chen sha256_checksum(fit, fdt_totalsize(fit), csum); 2782fb449a6SJoseph Chen ret = memcmp(hash, csum, SHA256_HASH_SIZE) ? -EINVAL : 0; 2792fb449a6SJoseph Chen if (ret) { 2802fb449a6SJoseph Chen printf(" update.hash: "); 2812fb449a6SJoseph Chen for (i = 0; i < SHA256_HASH_SIZE; i++) 2822fb449a6SJoseph Chen printf("%02x", hash[i]); 2832fb449a6SJoseph Chen printf("\n"); 2842fb449a6SJoseph Chen 2852fb449a6SJoseph Chen printf(" calculate hash: "); 2862fb449a6SJoseph Chen for (i = 0; i < SHA256_HASH_SIZE; i++) 2872fb449a6SJoseph Chen printf("%02x", csum[i]); 2882fb449a6SJoseph Chen printf("\n"); 2892fb449a6SJoseph Chen } 2902fb449a6SJoseph Chen 2912fb449a6SJoseph Chen return ret; 2922fb449a6SJoseph Chen } 2932fb449a6SJoseph Chen #endif 2942fb449a6SJoseph Chen 2952fb449a6SJoseph Chen static void print_hdr_local(struct update_header *hdr, 2962fb449a6SJoseph Chen struct local_information *local) 2972fb449a6SJoseph Chen { 2982fb449a6SJoseph Chen printf("# Server:\n"); 2992fb449a6SJoseph Chen printf(" version: %d\n", hdr->version); 3002fb449a6SJoseph Chen printf(" rollback_idx: %d\n", hdr->rollback_idx); 3012fb449a6SJoseph Chen printf(" force_update: %d\n", hdr->force_update); 3022fb449a6SJoseph Chen printf(" MB: %d\n", hdr->mb); 3032fb449a6SJoseph Chen printf(" lba_step: 0x%08x\n", hdr->lba_step); 3042fb449a6SJoseph Chen printf(" shared_buf: 0x%08lx - 0x%08lx\n", 3052fb449a6SJoseph Chen (ulong)hdr->shared_buf, (ulong)hdr->shared_buf + hdr->mb * SZ_1M); 3062fb449a6SJoseph Chen printf(" spec_partition: %s\n\n", hdr->spec_partition); 3072fb449a6SJoseph Chen 3082fb449a6SJoseph Chen printf("# Local:\n"); 3092fb449a6SJoseph Chen printf(" version: %d\n", local->version); 3102fb449a6SJoseph Chen printf(" rollback_idx: %d\n", local->rollback_idx); 3112fb449a6SJoseph Chen printf(" current_slot: %s\n", local->current_slot); 3122fb449a6SJoseph Chen printf("\n"); 3132fb449a6SJoseph Chen } 3142fb449a6SJoseph Chen 3152fb449a6SJoseph Chen static int hdr_param_verify(void *fit, struct update_header *hdr, 3162fb449a6SJoseph Chen struct local_information *local, int conf) 3172fb449a6SJoseph Chen { 3182fb449a6SJoseph Chen u32 size; 3192fb449a6SJoseph Chen int ret; 3202fb449a6SJoseph Chen 3212fb449a6SJoseph Chen /* remote */ 3222fb449a6SJoseph Chen hdr->version = fdtdec_get_uint(fit, 0, "version", 0); 3232fb449a6SJoseph Chen hdr->rollback_idx = fdtdec_get_uint(fit, conf, "rollback-index", 0); 3242fb449a6SJoseph Chen hdr->force_update = fdtdec_get_uint(fit, conf, "force_update", 0); 3252fb449a6SJoseph Chen hdr->mb = fdtdec_get_uint(fit, conf, "image-size-MB", 0); 3262fb449a6SJoseph Chen size = hdr->mb * SZ_1M; 3272fb449a6SJoseph Chen hdr->lba_step = size / 512; 3282fb449a6SJoseph Chen /* TODO: use sysmem alloc/free */ 3292fb449a6SJoseph Chen hdr->shared_buf = malloc(size); 3302fb449a6SJoseph Chen if (!hdr->shared_buf) 3312fb449a6SJoseph Chen return -ENOMEM; 3322fb449a6SJoseph Chen 3332fb449a6SJoseph Chen /* local */ 3342fb449a6SJoseph Chen ret = tftpfw_version_get(); 3352fb449a6SJoseph Chen if (ret < 0) { 3362fb449a6SJoseph Chen TFTPUD_E("Failed to get local firmware version, ret=%d\n", ret); 3372fb449a6SJoseph Chen return local->version; 3382fb449a6SJoseph Chen } 3392fb449a6SJoseph Chen local->version = ret; 3402fb449a6SJoseph Chen #ifdef CONFIG_FIT_ROLLBACK_PROTECT 3412fb449a6SJoseph Chen u32 remote_rollback_idx; 3422fb449a6SJoseph Chen 3432fb449a6SJoseph Chen ret = fit_rollback_index_verify(fit, FIT_ROLLBACK_INDEX, 3442fb449a6SJoseph Chen &remote_rollback_idx, &local->rollback_idx); 3452fb449a6SJoseph Chen if (ret) { 3462fb449a6SJoseph Chen TFTPUD_E("Failed to get local rollback-index, ret=%d\n", ret); 3472fb449a6SJoseph Chen return ret; 3482fb449a6SJoseph Chen } 3492fb449a6SJoseph Chen #else 3502fb449a6SJoseph Chen local->rollback_idx = -1; 3512fb449a6SJoseph Chen #endif 3522fb449a6SJoseph Chen #ifdef CONFIG_ANDROID_AB 3532fb449a6SJoseph Chen ret = rk_avb_get_current_slot(local->current_slot); 3542fb449a6SJoseph Chen if (ret) { 3552fb449a6SJoseph Chen TFTPUD_E("Failed to get local current slot, ret=%d\n", ret); 3562fb449a6SJoseph Chen return ret; 3572fb449a6SJoseph Chen } 3582fb449a6SJoseph Chen #else 3592fb449a6SJoseph Chen strcpy(local->current_slot, "-"); 3602fb449a6SJoseph Chen #endif 3612fb449a6SJoseph Chen 3622fb449a6SJoseph Chen print_hdr_local(hdr, local); 3632fb449a6SJoseph Chen 3642fb449a6SJoseph Chen /* verify */ 3652fb449a6SJoseph Chen if (hdr->force_update) { 3662fb449a6SJoseph Chen TFTPUD_I("Remote requires force upgrade !\n"); 3672fb449a6SJoseph Chen return 0; 3682fb449a6SJoseph Chen } 3692fb449a6SJoseph Chen if (hdr->version < local->version) { 3702fb449a6SJoseph Chen TFTPUD_E("Invalid firmware version: %d(remote) < %d(local)\n", 3712fb449a6SJoseph Chen hdr->version, local->version); 3722fb449a6SJoseph Chen return -EINVAL; 3732fb449a6SJoseph Chen } 3742fb449a6SJoseph Chen #ifdef CONFIG_FIT_ROLLBACK_PROTECT 3752fb449a6SJoseph Chen if (remote_rollback_idx < local->rollback_idx) { 3762fb449a6SJoseph Chen TFTPUD_E("Invalid rollback-index: %d(remote) < %d(local)\n", 3772fb449a6SJoseph Chen remote_rollback_idx, local->rollback_idx); 3782fb449a6SJoseph Chen return -EINVAL; 3792fb449a6SJoseph Chen } 3802fb449a6SJoseph Chen #endif 3812fb449a6SJoseph Chen 3822fb449a6SJoseph Chen return 0; 3832fb449a6SJoseph Chen } 3842fb449a6SJoseph Chen 3852fb449a6SJoseph Chen static int update_verify_hdr(void *fit, struct update_header *hdr, 3862fb449a6SJoseph Chen struct local_information *local) 3872fb449a6SJoseph Chen { 3882fb449a6SJoseph Chen const char *name; 3892fb449a6SJoseph Chen int noffset; 3902fb449a6SJoseph Chen int conf; 3912fb449a6SJoseph Chen int ret; 3922fb449a6SJoseph Chen 3932fb449a6SJoseph Chen noffset = fdt_path_offset(fit, FIT_CONFS_PATH); 3942fb449a6SJoseph Chen name = fdt_getprop(fit, noffset, "default", NULL); 3952fb449a6SJoseph Chen conf = fdt_subnode_offset(fit, noffset, name); 3962fb449a6SJoseph Chen if (conf < 0) 3972fb449a6SJoseph Chen return conf; 3982fb449a6SJoseph Chen 3992fb449a6SJoseph Chen #ifdef CONFIG_FIT_SIGNATURE 4002fb449a6SJoseph Chen /* Secure: verify signature */ 4012fb449a6SJoseph Chen ret = fit_config_verify(fit, conf); 4022fb449a6SJoseph Chen if (ret) 4032fb449a6SJoseph Chen return ret; 4042fb449a6SJoseph Chen 4052fb449a6SJoseph Chen TFTPUD_I("hdr signature verified\n"); 4062fb449a6SJoseph Chen #else 4072fb449a6SJoseph Chen /* Non-secure: verify hash */ 4082fb449a6SJoseph Chen ret = hdr_checksum_verify(fit, hdr); 4092fb449a6SJoseph Chen if (ret) 4102fb449a6SJoseph Chen return ret; 4112fb449a6SJoseph Chen 4122fb449a6SJoseph Chen TFTPUD_I("hdr checksum verified\n"); 4132fb449a6SJoseph Chen #endif 4142fb449a6SJoseph Chen /* verify rollback index ..., etc */ 4152fb449a6SJoseph Chen ret = hdr_param_verify(fit, hdr, local, conf); 4162fb449a6SJoseph Chen if (ret) 4172fb449a6SJoseph Chen return ret; 4182fb449a6SJoseph Chen 4192fb449a6SJoseph Chen TFTPUD_I("hdr param verified\n"); 4202fb449a6SJoseph Chen 4212fb449a6SJoseph Chen return 0; 4222fb449a6SJoseph Chen } 4232fb449a6SJoseph Chen 4242fb449a6SJoseph Chen static int update_local_info(void *fit, struct update_header *hdr) 4252fb449a6SJoseph Chen { 4262fb449a6SJoseph Chen int ret; 4272fb449a6SJoseph Chen 4282fb449a6SJoseph Chen TFTPUD_I("Update local information... "); 4292fb449a6SJoseph Chen 4302fb449a6SJoseph Chen ret = tftpfw_version_set(hdr->version); 4312fb449a6SJoseph Chen if (ret) { 4322fb449a6SJoseph Chen TFTPUD_E("Update local param FAIL, ret=%d\n", ret); 4332fb449a6SJoseph Chen return ret; 4342fb449a6SJoseph Chen } 4352fb449a6SJoseph Chen printf("fw_version=%d ", hdr->version); 4362fb449a6SJoseph Chen 4372fb449a6SJoseph Chen #ifdef CONFIG_FIT_ROLLBACK_PROTECT 4382fb449a6SJoseph Chen ret = fit_write_trusty_rollback_index(hdr->rollback_idx); 4392fb449a6SJoseph Chen if (ret) 4402fb449a6SJoseph Chen return ret; 4412fb449a6SJoseph Chen printf("rollback_idx=%d ", hdr->rollback_idx); 4422fb449a6SJoseph Chen #endif 4432fb449a6SJoseph Chen printf("\n"); 4442fb449a6SJoseph Chen 4452fb449a6SJoseph Chen return 0; 4462fb449a6SJoseph Chen } 4472fb449a6SJoseph Chen 4482fb449a6SJoseph Chen static int update_ignore_image(void *fit, struct update_header *hdr, 4492fb449a6SJoseph Chen struct image_element *e) 4502fb449a6SJoseph Chen { 4512fb449a6SJoseph Chen #ifdef CONFIG_ANDROID_AB 4522fb449a6SJoseph Chen char *slot_suffix; 4532fb449a6SJoseph Chen 4542fb449a6SJoseph Chen /* Android A/B skip current slot */ 4552fb449a6SJoseph Chen slot_suffix = (char *)e->part_name + strlen(e->part_name) - 2; 4562fb449a6SJoseph Chen if (!strcmp(hdr->current_slot, slot_suffix)) 4572fb449a6SJoseph Chen return 1; 4582fb449a6SJoseph Chen #endif 4592fb449a6SJoseph Chen /* try to find expected target partition */ 4602fb449a6SJoseph Chen if (hdr->spec_partition && strcmp(e->part_name, hdr->spec_partition)) 4612fb449a6SJoseph Chen return 1; 4622fb449a6SJoseph Chen 4632fb449a6SJoseph Chen return 0; 4642fb449a6SJoseph Chen } 4652fb449a6SJoseph Chen 4662fb449a6SJoseph Chen static int download_image(void *fit, struct image_element *e) 4672fb449a6SJoseph Chen { 4682fb449a6SJoseph Chen ulong fileaddr; 4692fb449a6SJoseph Chen ulong filesize; 4702fb449a6SJoseph Chen char *msg = ""; 4712fb449a6SJoseph Chen int ret; 4722fb449a6SJoseph Chen 4732fb449a6SJoseph Chen /* download */ 4742fb449a6SJoseph Chen printf("[TFTPUD-0]: download \"%s\" at 0x%lx\n", 4752fb449a6SJoseph Chen e->file_name, (ulong)e->buf); 4762fb449a6SJoseph Chen 4772fb449a6SJoseph Chen ret = tftp_download(e->buf, e->file_name); 4782fb449a6SJoseph Chen if (ret) 4792fb449a6SJoseph Chen return ret; 4802fb449a6SJoseph Chen 4812fb449a6SJoseph Chen fileaddr = env_get_ulong("fileaddr", 16, 0); 4822fb449a6SJoseph Chen filesize = env_get_ulong("filesize", 16, 0); 4832fb449a6SJoseph Chen if (!fileaddr || !filesize) { 4842fb449a6SJoseph Chen TFTPUD_E("No fileaddr and filesize\n"); 4852fb449a6SJoseph Chen return -ENOENT; 4862fb449a6SJoseph Chen } 4872fb449a6SJoseph Chen 4882fb449a6SJoseph Chen if (filesize != e->size) { 4892fb449a6SJoseph Chen TFTPUD_E("Expected filesize 0x%08lx != 0x%08x\n", filesize, e->size); 4902fb449a6SJoseph Chen return -EINVAL; 4912fb449a6SJoseph Chen } 4922fb449a6SJoseph Chen 4932fb449a6SJoseph Chen /* verify */ 4942fb449a6SJoseph Chen printf("[TFTPUD-1]: verify "); 4952fb449a6SJoseph Chen ret = fit_image_check_hash(fit, e->hash_noffset, e->buf, e->size, &msg); 4962fb449a6SJoseph Chen printf("[%s]\n", ret ? "-" : "+"); 4972fb449a6SJoseph Chen 4982fb449a6SJoseph Chen return ret; 4992fb449a6SJoseph Chen } 5002fb449a6SJoseph Chen 5012fb449a6SJoseph Chen static int update_flash_image(struct image_element *e) 5022fb449a6SJoseph Chen { 5032fb449a6SJoseph Chen struct blk_desc *dev_desc; 5042fb449a6SJoseph Chen int ret; 5052fb449a6SJoseph Chen 5062fb449a6SJoseph Chen dev_desc = rockchip_get_bootdev(); 5072fb449a6SJoseph Chen if (!dev_desc) { 5082fb449a6SJoseph Chen TFTPUD_E("No boot device\n"); 5092fb449a6SJoseph Chen return -ENODEV; 5102fb449a6SJoseph Chen } 5112fb449a6SJoseph Chen 5122fb449a6SJoseph Chen printf("[TFTPUD-2]: Flash to \"%s\" partition at LBA offset 0x%08x, " 5132fb449a6SJoseph Chen "with 0x%08x sectors ... ", 5142fb449a6SJoseph Chen e->part_name, e->lba_offset, e->lba_cnt); 5152fb449a6SJoseph Chen 5162fb449a6SJoseph Chen if (dev_desc->if_type == IF_TYPE_MTD) { 5172fb449a6SJoseph Chen dev_desc->op_flag |= BLK_MTD_CONT_WRITE; 5182fb449a6SJoseph Chen ret = blk_dwrite(dev_desc, e->lba_start + e->lba_offset, 5192fb449a6SJoseph Chen e->lba_cnt, (void *)e->buf); 5202fb449a6SJoseph Chen dev_desc->op_flag &= ~(BLK_MTD_CONT_WRITE); 5212fb449a6SJoseph Chen } else { 5222fb449a6SJoseph Chen ret = blk_dwrite(dev_desc, e->lba_start + e->lba_offset, 5232fb449a6SJoseph Chen e->lba_cnt, (void *)e->buf); 5242fb449a6SJoseph Chen } 5252fb449a6SJoseph Chen 5262fb449a6SJoseph Chen if (ret != e->lba_cnt) 5272fb449a6SJoseph Chen printf("Failed(%d)\n\n\n", ret); 5282fb449a6SJoseph Chen else 5292fb449a6SJoseph Chen printf("OK\n\n\n"); 5302fb449a6SJoseph Chen 5312fb449a6SJoseph Chen return 0; 5322fb449a6SJoseph Chen } 5332fb449a6SJoseph Chen 5342fb449a6SJoseph Chen static int update_download_image(void *fit, struct image_element *e) 5352fb449a6SJoseph Chen { 5362fb449a6SJoseph Chen int i, ret; 5372fb449a6SJoseph Chen 5382fb449a6SJoseph Chen for (i = 0; i < e->remain_tries; i++) { 5392fb449a6SJoseph Chen ret = download_image(fit, e); 5402fb449a6SJoseph Chen if (!ret) 5412fb449a6SJoseph Chen return 0; 5422fb449a6SJoseph Chen 5432fb449a6SJoseph Chen TFTPUD_E("retry-%d download\n", i); 5442fb449a6SJoseph Chen continue; 5452fb449a6SJoseph Chen } 5462fb449a6SJoseph Chen 5472fb449a6SJoseph Chen return -ENODATA; 5482fb449a6SJoseph Chen } 5492fb449a6SJoseph Chen 5502fb449a6SJoseph Chen static int update_write_gpt(void *fit, struct update_header *hdr) 5512fb449a6SJoseph Chen { 5522fb449a6SJoseph Chen struct image_element *e; 5532fb449a6SJoseph Chen char *gpt_parts, *p; 5542fb449a6SJoseph Chen const char *name; 5552fb449a6SJoseph Chen int images; 5562fb449a6SJoseph Chen int noffset; 5572fb449a6SJoseph Chen int ret = 0; 5582fb449a6SJoseph Chen 5592fb449a6SJoseph Chen images = fdt_path_offset(fit, FIT_IMAGES_PATH); 5602fb449a6SJoseph Chen if (images < 0) 5612fb449a6SJoseph Chen return images; 5622fb449a6SJoseph Chen 5632fb449a6SJoseph Chen noffset = fdt_first_subnode(fit, images); 5642fb449a6SJoseph Chen if (noffset < 0) 5652fb449a6SJoseph Chen return noffset; 5662fb449a6SJoseph Chen 5672fb449a6SJoseph Chen /* gpt must be the 1st node */ 5682fb449a6SJoseph Chen name = fit_get_name(fit, noffset, NULL); 5692fb449a6SJoseph Chen if (!is_gpt(name)) 5702fb449a6SJoseph Chen return 0; 5712fb449a6SJoseph Chen 5722fb449a6SJoseph Chen e = malloc(sizeof(*e)); 5732fb449a6SJoseph Chen if (!e) 5742fb449a6SJoseph Chen return -ENOMEM; 5752fb449a6SJoseph Chen 5762fb449a6SJoseph Chen e->remain_tries = MAX_REMAIN_TRIES; 5772fb449a6SJoseph Chen e->buf = hdr->shared_buf; 5782fb449a6SJoseph Chen e->size = fdtdec_get_uint(fit, noffset, "data-size", -ENODATA); 5792fb449a6SJoseph Chen if (e->size == -ENODATA) { 5802fb449a6SJoseph Chen ret = -EINVAL; 5812fb449a6SJoseph Chen goto out; 5822fb449a6SJoseph Chen } 5832fb449a6SJoseph Chen 5842fb449a6SJoseph Chen strcpy(e->file_name, name); 5852fb449a6SJoseph Chen e->hash_noffset = fdt_subnode_offset(fit, noffset, "hash"); 5862fb449a6SJoseph Chen if (e->hash_noffset < 0) 5872fb449a6SJoseph Chen return e->hash_noffset; 5882fb449a6SJoseph Chen 5892fb449a6SJoseph Chen printf("\n# %s:\n", e->file_name); 5902fb449a6SJoseph Chen printf(" buf: 0x%08lx\n", (ulong)e->buf); 5912fb449a6SJoseph Chen printf(" size: 0x%08x\n", e->size); 5922fb449a6SJoseph Chen printf(" remain_tries: %d\n", e->remain_tries); 5932fb449a6SJoseph Chen printf(" hash_noffset: 0x%08x\n\n", e->hash_noffset); 5942fb449a6SJoseph Chen 5952fb449a6SJoseph Chen /* download */ 5962fb449a6SJoseph Chen ret = update_download_image(fit, e); 5972fb449a6SJoseph Chen if (ret) { 5982fb449a6SJoseph Chen TFTPUD_E("\"%s\" download fail, ret=%d\n", 5992fb449a6SJoseph Chen e->file_name, ret); 6002fb449a6SJoseph Chen goto out; 6012fb449a6SJoseph Chen } 6022fb449a6SJoseph Chen 6032fb449a6SJoseph Chen /* terminate gpt string */ 6042fb449a6SJoseph Chen gpt_parts = (char *)e->buf; 6052fb449a6SJoseph Chen p = gpt_parts + e->size - 1; 6062fb449a6SJoseph Chen *p = '\0'; 6072fb449a6SJoseph Chen 6082fb449a6SJoseph Chen /* write */ 6092fb449a6SJoseph Chen printf("[TFTPUD-2]: Write gpt ...\n"); 6102fb449a6SJoseph Chen printf(" %s\n\n", gpt_parts); 6112fb449a6SJoseph Chen env_set("gpt_parts", gpt_parts); 6122fb449a6SJoseph Chen ret = run_command("gpt write ${devtype} ${devnum} ${gpt_parts}", 0); 6132fb449a6SJoseph Chen if (ret) { 6142fb449a6SJoseph Chen printf("Failed to write gpt\n"); 6152fb449a6SJoseph Chen ret = -EIO; 6162fb449a6SJoseph Chen goto out; 6172fb449a6SJoseph Chen } 6182fb449a6SJoseph Chen ret = run_command("gpt verify ${devtype} ${devnum} ${gpt_parts}", 0); 6192fb449a6SJoseph Chen if (ret) { 6202fb449a6SJoseph Chen printf("Failed to verify gpt\n"); 6212fb449a6SJoseph Chen ret = -EIO; 6222fb449a6SJoseph Chen goto out; 6232fb449a6SJoseph Chen } 6242fb449a6SJoseph Chen printf("\n"); 6252fb449a6SJoseph Chen out: 6262fb449a6SJoseph Chen free(e); 6272fb449a6SJoseph Chen 6282fb449a6SJoseph Chen return ret; 6292fb449a6SJoseph Chen } 6302fb449a6SJoseph Chen 6312fb449a6SJoseph Chen static int do_tftp_update(cmd_tbl_t *cmdtp, int flag, 6322fb449a6SJoseph Chen int argc, char * const argv[]) 6332fb449a6SJoseph Chen { 6342fb449a6SJoseph Chen struct local_information *local = &local_info; 6352fb449a6SJoseph Chen struct update_header *hdr = &update_hdr; 6362fb449a6SJoseph Chen struct image_element *e; 6372fb449a6SJoseph Chen struct list_head *node; 6382fb449a6SJoseph Chen const char *dir_part_str; 6392fb449a6SJoseph Chen const char *part_str; 6402fb449a6SJoseph Chen const char *dir_str; 6412fb449a6SJoseph Chen char *dup_str = NULL; 6422fb449a6SJoseph Chen u32 total_success = 0; 6432fb449a6SJoseph Chen u32 total_traverse = 0; 6442fb449a6SJoseph Chen ulong start_ms; 6452fb449a6SJoseph Chen ulong total_ms; 6462fb449a6SJoseph Chen void *fit; 6472fb449a6SJoseph Chen int ret; 6482fb449a6SJoseph Chen 6492fb449a6SJoseph Chen start_ms = get_timer(0); 6502fb449a6SJoseph Chen memset(hdr, 0, sizeof(*hdr)); 6512fb449a6SJoseph Chen memset(local, 0, sizeof(*local)); 6522fb449a6SJoseph Chen 6532fb449a6SJoseph Chen /* only handle a single partititon ? */ 6542fb449a6SJoseph Chen if (argc > 1) { 6552fb449a6SJoseph Chen dir_part_str = argv[1]; 6562fb449a6SJoseph Chen part_str = strchr(dir_part_str, ':'); 6572fb449a6SJoseph Chen if (part_str) { 6582fb449a6SJoseph Chen /* 6592fb449a6SJoseph Chen * eg: tftpupdate image:recovery 6602fb449a6SJoseph Chen * tftpupdate image:* 6612fb449a6SJoseph Chen * tftpupdate image: 6622fb449a6SJoseph Chen */ 6632fb449a6SJoseph Chen dup_str = strdup(dir_part_str); 6642fb449a6SJoseph Chen dup_str[part_str - dir_part_str] = 0; 6652fb449a6SJoseph Chen dir_str = dup_str; 6662fb449a6SJoseph Chen part_str++; 6672fb449a6SJoseph Chen if (*part_str == '*') 6682fb449a6SJoseph Chen part_str = NULL; 6692fb449a6SJoseph Chen } else { 6702fb449a6SJoseph Chen /* eg: tftpupdate recovery */ 6712fb449a6SJoseph Chen dir_str = NULL; 6722fb449a6SJoseph Chen part_str = argv[1]; 6732fb449a6SJoseph Chen } 6742fb449a6SJoseph Chen } else { 6752fb449a6SJoseph Chen dir_str = NULL; 6762fb449a6SJoseph Chen part_str = NULL; 6772fb449a6SJoseph Chen } 6782fb449a6SJoseph Chen 6792fb449a6SJoseph Chen server_dir = dir_str; 6802fb449a6SJoseph Chen hdr->spec_partition = part_str; 6812fb449a6SJoseph Chen INIT_LIST_HEAD(&hdr->images); 6822fb449a6SJoseph Chen 6832fb449a6SJoseph Chen fit = update_download_hdr(hdr); 6842fb449a6SJoseph Chen if (!fit) { 6852fb449a6SJoseph Chen TFTPUD_E("download hdr fail\n"); 6862fb449a6SJoseph Chen ret = -EINVAL; 6872fb449a6SJoseph Chen goto out; 6882fb449a6SJoseph Chen } 6892fb449a6SJoseph Chen 6902fb449a6SJoseph Chen ret = update_verify_hdr(fit, hdr, local); 6912fb449a6SJoseph Chen if (ret) { 6922fb449a6SJoseph Chen TFTPUD_E("verify hdr fail, ret=%d\n", ret); 6932fb449a6SJoseph Chen goto out; 6942fb449a6SJoseph Chen } 6952fb449a6SJoseph Chen 6962fb449a6SJoseph Chen /* flash gpt table early than any other partition */ 6972fb449a6SJoseph Chen ret = update_write_gpt(fit, hdr); 6982fb449a6SJoseph Chen if (ret) { 6992fb449a6SJoseph Chen TFTPUD_E("write gpt fail, ret=%d\n", ret); 7002fb449a6SJoseph Chen goto out; 7012fb449a6SJoseph Chen } 7022fb449a6SJoseph Chen 7032fb449a6SJoseph Chen ret = update_populate_image(fit, hdr); 7042fb449a6SJoseph Chen if (ret) { 7052fb449a6SJoseph Chen TFTPUD_E("populate image fail, ret=%d\n", ret); 7062fb449a6SJoseph Chen goto out; 7072fb449a6SJoseph Chen } 7082fb449a6SJoseph Chen 7092fb449a6SJoseph Chen list_for_each(node, &hdr->images) { 7102fb449a6SJoseph Chen e = list_entry(node, struct image_element, node); 7112fb449a6SJoseph Chen total_traverse++; 7122fb449a6SJoseph Chen 7132fb449a6SJoseph Chen /* ignore ? */ 7142fb449a6SJoseph Chen if (update_ignore_image(fit, hdr, e)) 7152fb449a6SJoseph Chen continue; 7162fb449a6SJoseph Chen 7172fb449a6SJoseph Chen ret = update_download_image(fit, e); 7182fb449a6SJoseph Chen if (ret) { 7192fb449a6SJoseph Chen TFTPUD_E("\"%s\" download fail, ret=%d\n", 7202fb449a6SJoseph Chen e->file_name, ret); 7212fb449a6SJoseph Chen goto out; 7222fb449a6SJoseph Chen } 7232fb449a6SJoseph Chen 7242fb449a6SJoseph Chen ret = update_flash_image(e); 7252fb449a6SJoseph Chen if (ret) { 7262fb449a6SJoseph Chen TFTPUD_E("\"%s\" flash fail, ret=%d\n", 7272fb449a6SJoseph Chen e->file_name, ret); 7282fb449a6SJoseph Chen goto out; 7292fb449a6SJoseph Chen } 7302fb449a6SJoseph Chen 7312fb449a6SJoseph Chen total_success++; 7322fb449a6SJoseph Chen } 7332fb449a6SJoseph Chen 7342fb449a6SJoseph Chen if (total_success == 0) { 7352fb449a6SJoseph Chen if (hdr->spec_partition) { 7362fb449a6SJoseph Chen TFTPUD_E("No %s partition was found\n", hdr->spec_partition); 7372fb449a6SJoseph Chen ret = CMD_RET_FAILURE; 7382fb449a6SJoseph Chen } 7392fb449a6SJoseph Chen goto out; 7402fb449a6SJoseph Chen } 7412fb449a6SJoseph Chen 7422fb449a6SJoseph Chen /* If this is full upgrade, update local info */ 7432fb449a6SJoseph Chen if (!hdr->spec_partition) 7442fb449a6SJoseph Chen update_local_info(fit, hdr); 7452fb449a6SJoseph Chen out: 7462fb449a6SJoseph Chen update_cleanup(fit, hdr); 7472fb449a6SJoseph Chen if (!ret) { 7482fb449a6SJoseph Chen total_ms = get_timer(start_ms); 7492fb449a6SJoseph Chen TFTPUD_I("tftpupdate is OK (total time: %lds, upgrade: %d/%d), " 7502fb449a6SJoseph Chen "system reboot is recommend\n", 7512fb449a6SJoseph Chen total_ms / 1000, total_success, total_traverse); 7522fb449a6SJoseph Chen } 7532fb449a6SJoseph Chen 7542fb449a6SJoseph Chen return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS; 7552fb449a6SJoseph Chen } 7562fb449a6SJoseph Chen 7572fb449a6SJoseph Chen U_BOOT_CMD( 7582fb449a6SJoseph Chen tftpupdate, 2, 1, do_tftp_update, 7592fb449a6SJoseph Chen "Update a set of images organized with FIT via network using TFTP protocol", 7602fb449a6SJoseph Chen "[[server-dir:][partition]" 7612fb449a6SJoseph Chen ); 7622fb449a6SJoseph Chen 763