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
tftpfw_version_set(u32 version)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
tftpfw_version_get(void)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 */
82892e9cd5SJoseph 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
tftp_download(void * addr,const char * file)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
update_cleanup(void * fit,struct update_header * hdr)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)
118687d7ae3SJoseph Chen free(hdr->shared_buf);
1192fb449a6SJoseph Chen if (fit)
1202fb449a6SJoseph Chen free(fit);
1212fb449a6SJoseph Chen }
1222fb449a6SJoseph Chen
is_gpt(const char * name)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
update_populate_image(void * fit,struct update_header * hdr)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
update_download_hdr(struct update_header * hdr)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
hdr_checksum_verify(void * fit,struct update_header * hdr)2492fb449a6SJoseph Chen static int hdr_checksum_verify(void *fit, struct update_header *hdr)
2502fb449a6SJoseph Chen {
2512fb449a6SJoseph Chen u8 *hash, csum[SHA256_HASH_SIZE];
2522fb449a6SJoseph Chen int ret, i;
2532fb449a6SJoseph Chen
2542fb449a6SJoseph Chen hash = (u8 *)fit + fdt_totalsize(fit);
255*4e0a26e7SJoseph Chen sha256_csum((const uchar *)fit, fdt_totalsize(fit), csum);
2562fb449a6SJoseph Chen ret = memcmp(hash, csum, SHA256_HASH_SIZE) ? -EINVAL : 0;
2572fb449a6SJoseph Chen if (ret) {
2582fb449a6SJoseph Chen printf(" update.hash: ");
2592fb449a6SJoseph Chen for (i = 0; i < SHA256_HASH_SIZE; i++)
2602fb449a6SJoseph Chen printf("%02x", hash[i]);
2612fb449a6SJoseph Chen printf("\n");
2622fb449a6SJoseph Chen
2632fb449a6SJoseph Chen printf(" calculate hash: ");
2642fb449a6SJoseph Chen for (i = 0; i < SHA256_HASH_SIZE; i++)
2652fb449a6SJoseph Chen printf("%02x", csum[i]);
2662fb449a6SJoseph Chen printf("\n");
2672fb449a6SJoseph Chen }
2682fb449a6SJoseph Chen
2692fb449a6SJoseph Chen return ret;
2702fb449a6SJoseph Chen }
2712fb449a6SJoseph Chen #endif
2722fb449a6SJoseph Chen
print_hdr_local(struct update_header * hdr,struct local_information * local)2732fb449a6SJoseph Chen static void print_hdr_local(struct update_header *hdr,
2742fb449a6SJoseph Chen struct local_information *local)
2752fb449a6SJoseph Chen {
2762fb449a6SJoseph Chen printf("# Server:\n");
2772fb449a6SJoseph Chen printf(" version: %d\n", hdr->version);
2782fb449a6SJoseph Chen printf(" rollback_idx: %d\n", hdr->rollback_idx);
2792fb449a6SJoseph Chen printf(" force_update: %d\n", hdr->force_update);
2802fb449a6SJoseph Chen printf(" MB: %d\n", hdr->mb);
2812fb449a6SJoseph Chen printf(" lba_step: 0x%08x\n", hdr->lba_step);
2822fb449a6SJoseph Chen printf(" shared_buf: 0x%08lx - 0x%08lx\n",
2832fb449a6SJoseph Chen (ulong)hdr->shared_buf, (ulong)hdr->shared_buf + hdr->mb * SZ_1M);
2842fb449a6SJoseph Chen printf(" spec_partition: %s\n\n", hdr->spec_partition);
2852fb449a6SJoseph Chen
2862fb449a6SJoseph Chen printf("# Local:\n");
2872fb449a6SJoseph Chen printf(" version: %d\n", local->version);
2882fb449a6SJoseph Chen printf(" rollback_idx: %d\n", local->rollback_idx);
2892fb449a6SJoseph Chen printf(" current_slot: %s\n", local->current_slot);
2902fb449a6SJoseph Chen printf("\n");
2912fb449a6SJoseph Chen }
2922fb449a6SJoseph Chen
hdr_param_verify(void * fit,struct update_header * hdr,struct local_information * local,int conf)2932fb449a6SJoseph Chen static int hdr_param_verify(void *fit, struct update_header *hdr,
2942fb449a6SJoseph Chen struct local_information *local, int conf)
2952fb449a6SJoseph Chen {
2962fb449a6SJoseph Chen u32 size;
2972fb449a6SJoseph Chen int ret;
2982fb449a6SJoseph Chen
2992fb449a6SJoseph Chen /* remote */
3002fb449a6SJoseph Chen hdr->version = fdtdec_get_uint(fit, 0, "version", 0);
3012fb449a6SJoseph Chen hdr->rollback_idx = fdtdec_get_uint(fit, conf, "rollback-index", 0);
3022fb449a6SJoseph Chen hdr->force_update = fdtdec_get_uint(fit, conf, "force_update", 0);
3032fb449a6SJoseph Chen hdr->mb = fdtdec_get_uint(fit, conf, "image-size-MB", 0);
3042fb449a6SJoseph Chen size = hdr->mb * SZ_1M;
3052fb449a6SJoseph Chen hdr->lba_step = size / 512;
3062fb449a6SJoseph Chen /* TODO: use sysmem alloc/free */
3072fb449a6SJoseph Chen hdr->shared_buf = malloc(size);
3082fb449a6SJoseph Chen if (!hdr->shared_buf)
3092fb449a6SJoseph Chen return -ENOMEM;
3102fb449a6SJoseph Chen
3112fb449a6SJoseph Chen /* local */
3122fb449a6SJoseph Chen ret = tftpfw_version_get();
3132fb449a6SJoseph Chen if (ret < 0) {
3142fb449a6SJoseph Chen TFTPUD_E("Failed to get local firmware version, ret=%d\n", ret);
3152fb449a6SJoseph Chen return local->version;
3162fb449a6SJoseph Chen }
3172fb449a6SJoseph Chen local->version = ret;
3182fb449a6SJoseph Chen #ifdef CONFIG_FIT_ROLLBACK_PROTECT
3192fb449a6SJoseph Chen u32 remote_rollback_idx;
3202fb449a6SJoseph Chen
3212fb449a6SJoseph Chen ret = fit_rollback_index_verify(fit, FIT_ROLLBACK_INDEX,
3222fb449a6SJoseph Chen &remote_rollback_idx, &local->rollback_idx);
3232fb449a6SJoseph Chen if (ret) {
3242fb449a6SJoseph Chen TFTPUD_E("Failed to get local rollback-index, ret=%d\n", ret);
3252fb449a6SJoseph Chen return ret;
3262fb449a6SJoseph Chen }
3272fb449a6SJoseph Chen #else
3282fb449a6SJoseph Chen local->rollback_idx = -1;
3292fb449a6SJoseph Chen #endif
3302fb449a6SJoseph Chen #ifdef CONFIG_ANDROID_AB
3312fb449a6SJoseph Chen ret = rk_avb_get_current_slot(local->current_slot);
3322fb449a6SJoseph Chen if (ret) {
3332fb449a6SJoseph Chen TFTPUD_E("Failed to get local current slot, ret=%d\n", ret);
3342fb449a6SJoseph Chen return ret;
3352fb449a6SJoseph Chen }
3362fb449a6SJoseph Chen #else
3372fb449a6SJoseph Chen strcpy(local->current_slot, "-");
3382fb449a6SJoseph Chen #endif
3392fb449a6SJoseph Chen
3402fb449a6SJoseph Chen print_hdr_local(hdr, local);
3412fb449a6SJoseph Chen
3422fb449a6SJoseph Chen /* verify */
3432fb449a6SJoseph Chen if (hdr->force_update) {
3442fb449a6SJoseph Chen TFTPUD_I("Remote requires force upgrade !\n");
3452fb449a6SJoseph Chen return 0;
3462fb449a6SJoseph Chen }
3472fb449a6SJoseph Chen if (hdr->version < local->version) {
3482fb449a6SJoseph Chen TFTPUD_E("Invalid firmware version: %d(remote) < %d(local)\n",
3492fb449a6SJoseph Chen hdr->version, local->version);
3502fb449a6SJoseph Chen return -EINVAL;
3512fb449a6SJoseph Chen }
3522fb449a6SJoseph Chen #ifdef CONFIG_FIT_ROLLBACK_PROTECT
3532fb449a6SJoseph Chen if (remote_rollback_idx < local->rollback_idx) {
3542fb449a6SJoseph Chen TFTPUD_E("Invalid rollback-index: %d(remote) < %d(local)\n",
3552fb449a6SJoseph Chen remote_rollback_idx, local->rollback_idx);
3562fb449a6SJoseph Chen return -EINVAL;
3572fb449a6SJoseph Chen }
3582fb449a6SJoseph Chen #endif
3592fb449a6SJoseph Chen
3602fb449a6SJoseph Chen return 0;
3612fb449a6SJoseph Chen }
3622fb449a6SJoseph Chen
update_verify_hdr(void * fit,struct update_header * hdr,struct local_information * local)3632fb449a6SJoseph Chen static int update_verify_hdr(void *fit, struct update_header *hdr,
3642fb449a6SJoseph Chen struct local_information *local)
3652fb449a6SJoseph Chen {
3662fb449a6SJoseph Chen const char *name;
3672fb449a6SJoseph Chen int noffset;
3682fb449a6SJoseph Chen int conf;
3692fb449a6SJoseph Chen int ret;
3702fb449a6SJoseph Chen
3712fb449a6SJoseph Chen noffset = fdt_path_offset(fit, FIT_CONFS_PATH);
3722fb449a6SJoseph Chen name = fdt_getprop(fit, noffset, "default", NULL);
3732fb449a6SJoseph Chen conf = fdt_subnode_offset(fit, noffset, name);
3742fb449a6SJoseph Chen if (conf < 0)
3752fb449a6SJoseph Chen return conf;
3762fb449a6SJoseph Chen
3772fb449a6SJoseph Chen #ifdef CONFIG_FIT_SIGNATURE
3782fb449a6SJoseph Chen /* Secure: verify signature */
3792fb449a6SJoseph Chen ret = fit_config_verify(fit, conf);
3802fb449a6SJoseph Chen if (ret)
3812fb449a6SJoseph Chen return ret;
3822fb449a6SJoseph Chen
3832fb449a6SJoseph Chen TFTPUD_I("hdr signature verified\n");
3842fb449a6SJoseph Chen #else
3852fb449a6SJoseph Chen /* Non-secure: verify hash */
3862fb449a6SJoseph Chen ret = hdr_checksum_verify(fit, hdr);
3872fb449a6SJoseph Chen if (ret)
3882fb449a6SJoseph Chen return ret;
3892fb449a6SJoseph Chen
3902fb449a6SJoseph Chen TFTPUD_I("hdr checksum verified\n");
3912fb449a6SJoseph Chen #endif
3922fb449a6SJoseph Chen /* verify rollback index ..., etc */
3932fb449a6SJoseph Chen ret = hdr_param_verify(fit, hdr, local, conf);
3942fb449a6SJoseph Chen if (ret)
3952fb449a6SJoseph Chen return ret;
3962fb449a6SJoseph Chen
3972fb449a6SJoseph Chen TFTPUD_I("hdr param verified\n");
3982fb449a6SJoseph Chen
3992fb449a6SJoseph Chen return 0;
4002fb449a6SJoseph Chen }
4012fb449a6SJoseph Chen
update_local_info(void * fit,struct update_header * hdr)4022fb449a6SJoseph Chen static int update_local_info(void *fit, struct update_header *hdr)
4032fb449a6SJoseph Chen {
4042fb449a6SJoseph Chen int ret;
4052fb449a6SJoseph Chen
4062fb449a6SJoseph Chen TFTPUD_I("Update local information... ");
4072fb449a6SJoseph Chen
4082fb449a6SJoseph Chen ret = tftpfw_version_set(hdr->version);
4092fb449a6SJoseph Chen if (ret) {
4102fb449a6SJoseph Chen TFTPUD_E("Update local param FAIL, ret=%d\n", ret);
4112fb449a6SJoseph Chen return ret;
4122fb449a6SJoseph Chen }
4132fb449a6SJoseph Chen printf("fw_version=%d ", hdr->version);
4142fb449a6SJoseph Chen
4152fb449a6SJoseph Chen #ifdef CONFIG_FIT_ROLLBACK_PROTECT
4162fb449a6SJoseph Chen ret = fit_write_trusty_rollback_index(hdr->rollback_idx);
4172fb449a6SJoseph Chen if (ret)
4182fb449a6SJoseph Chen return ret;
4192fb449a6SJoseph Chen printf("rollback_idx=%d ", hdr->rollback_idx);
4202fb449a6SJoseph Chen #endif
4212fb449a6SJoseph Chen printf("\n");
4222fb449a6SJoseph Chen
4232fb449a6SJoseph Chen return 0;
4242fb449a6SJoseph Chen }
4252fb449a6SJoseph Chen
update_ignore_image(void * fit,struct update_header * hdr,struct image_element * e)4262fb449a6SJoseph Chen static int update_ignore_image(void *fit, struct update_header *hdr,
4272fb449a6SJoseph Chen struct image_element *e)
4282fb449a6SJoseph Chen {
4292fb449a6SJoseph Chen #ifdef CONFIG_ANDROID_AB
4302fb449a6SJoseph Chen char *slot_suffix;
4312fb449a6SJoseph Chen
4322fb449a6SJoseph Chen /* Android A/B skip current slot */
4332fb449a6SJoseph Chen slot_suffix = (char *)e->part_name + strlen(e->part_name) - 2;
4342fb449a6SJoseph Chen if (!strcmp(hdr->current_slot, slot_suffix))
4352fb449a6SJoseph Chen return 1;
4362fb449a6SJoseph Chen #endif
4372fb449a6SJoseph Chen /* try to find expected target partition */
4382fb449a6SJoseph Chen if (hdr->spec_partition && strcmp(e->part_name, hdr->spec_partition))
4392fb449a6SJoseph Chen return 1;
4402fb449a6SJoseph Chen
4412fb449a6SJoseph Chen return 0;
4422fb449a6SJoseph Chen }
4432fb449a6SJoseph Chen
download_image(void * fit,struct image_element * e)4442fb449a6SJoseph Chen static int download_image(void *fit, struct image_element *e)
4452fb449a6SJoseph Chen {
4462fb449a6SJoseph Chen ulong fileaddr;
4472fb449a6SJoseph Chen ulong filesize;
4482fb449a6SJoseph Chen char *msg = "";
4492fb449a6SJoseph Chen int ret;
4502fb449a6SJoseph Chen
4512fb449a6SJoseph Chen /* download */
4522fb449a6SJoseph Chen printf("[TFTPUD-0]: download \"%s\" at 0x%lx\n",
4532fb449a6SJoseph Chen e->file_name, (ulong)e->buf);
4542fb449a6SJoseph Chen
4552fb449a6SJoseph Chen ret = tftp_download(e->buf, e->file_name);
4562fb449a6SJoseph Chen if (ret)
4572fb449a6SJoseph Chen return ret;
4582fb449a6SJoseph Chen
4592fb449a6SJoseph Chen fileaddr = env_get_ulong("fileaddr", 16, 0);
4602fb449a6SJoseph Chen filesize = env_get_ulong("filesize", 16, 0);
4612fb449a6SJoseph Chen if (!fileaddr || !filesize) {
4622fb449a6SJoseph Chen TFTPUD_E("No fileaddr and filesize\n");
4632fb449a6SJoseph Chen return -ENOENT;
4642fb449a6SJoseph Chen }
4652fb449a6SJoseph Chen
4662fb449a6SJoseph Chen if (filesize != e->size) {
4672fb449a6SJoseph Chen TFTPUD_E("Expected filesize 0x%08lx != 0x%08x\n", filesize, e->size);
4682fb449a6SJoseph Chen return -EINVAL;
4692fb449a6SJoseph Chen }
4702fb449a6SJoseph Chen
4712fb449a6SJoseph Chen /* verify */
4722fb449a6SJoseph Chen printf("[TFTPUD-1]: verify ");
4732fb449a6SJoseph Chen ret = fit_image_check_hash(fit, e->hash_noffset, e->buf, e->size, &msg);
4742fb449a6SJoseph Chen printf("[%s]\n", ret ? "-" : "+");
4752fb449a6SJoseph Chen
4762fb449a6SJoseph Chen return ret;
4772fb449a6SJoseph Chen }
4782fb449a6SJoseph Chen
update_flash_image(struct image_element * e)4792fb449a6SJoseph Chen static int update_flash_image(struct image_element *e)
4802fb449a6SJoseph Chen {
4812fb449a6SJoseph Chen struct blk_desc *dev_desc;
4822fb449a6SJoseph Chen int ret;
4832fb449a6SJoseph Chen
4842fb449a6SJoseph Chen dev_desc = rockchip_get_bootdev();
4852fb449a6SJoseph Chen if (!dev_desc) {
4862fb449a6SJoseph Chen TFTPUD_E("No boot device\n");
4872fb449a6SJoseph Chen return -ENODEV;
4882fb449a6SJoseph Chen }
4892fb449a6SJoseph Chen
4902fb449a6SJoseph Chen printf("[TFTPUD-2]: Flash to \"%s\" partition at LBA offset 0x%08x, "
4912fb449a6SJoseph Chen "with 0x%08x sectors ... ",
4922fb449a6SJoseph Chen e->part_name, e->lba_offset, e->lba_cnt);
4932fb449a6SJoseph Chen
4942fb449a6SJoseph Chen if (dev_desc->if_type == IF_TYPE_MTD) {
4952fb449a6SJoseph Chen dev_desc->op_flag |= BLK_MTD_CONT_WRITE;
4962fb449a6SJoseph Chen ret = blk_dwrite(dev_desc, e->lba_start + e->lba_offset,
4972fb449a6SJoseph Chen e->lba_cnt, (void *)e->buf);
4982fb449a6SJoseph Chen dev_desc->op_flag &= ~(BLK_MTD_CONT_WRITE);
4992fb449a6SJoseph Chen } else {
5002fb449a6SJoseph Chen ret = blk_dwrite(dev_desc, e->lba_start + e->lba_offset,
5012fb449a6SJoseph Chen e->lba_cnt, (void *)e->buf);
5022fb449a6SJoseph Chen }
5032fb449a6SJoseph Chen
5042fb449a6SJoseph Chen if (ret != e->lba_cnt)
5052fb449a6SJoseph Chen printf("Failed(%d)\n\n\n", ret);
5062fb449a6SJoseph Chen else
5072fb449a6SJoseph Chen printf("OK\n\n\n");
5082fb449a6SJoseph Chen
5092fb449a6SJoseph Chen return 0;
5102fb449a6SJoseph Chen }
5112fb449a6SJoseph Chen
update_download_image(void * fit,struct image_element * e)5122fb449a6SJoseph Chen static int update_download_image(void *fit, struct image_element *e)
5132fb449a6SJoseph Chen {
5142fb449a6SJoseph Chen int i, ret;
5152fb449a6SJoseph Chen
5162fb449a6SJoseph Chen for (i = 0; i < e->remain_tries; i++) {
5172fb449a6SJoseph Chen ret = download_image(fit, e);
5182fb449a6SJoseph Chen if (!ret)
5192fb449a6SJoseph Chen return 0;
5202fb449a6SJoseph Chen
5212fb449a6SJoseph Chen TFTPUD_E("retry-%d download\n", i);
5222fb449a6SJoseph Chen continue;
5232fb449a6SJoseph Chen }
5242fb449a6SJoseph Chen
5252fb449a6SJoseph Chen return -ENODATA;
5262fb449a6SJoseph Chen }
5272fb449a6SJoseph Chen
update_write_gpt(void * fit,struct update_header * hdr)5282fb449a6SJoseph Chen static int update_write_gpt(void *fit, struct update_header *hdr)
5292fb449a6SJoseph Chen {
5302fb449a6SJoseph Chen struct image_element *e;
5312fb449a6SJoseph Chen char *gpt_parts, *p;
5322fb449a6SJoseph Chen const char *name;
5332fb449a6SJoseph Chen int images;
5342fb449a6SJoseph Chen int noffset;
5352fb449a6SJoseph Chen int ret = 0;
5362fb449a6SJoseph Chen
5372fb449a6SJoseph Chen images = fdt_path_offset(fit, FIT_IMAGES_PATH);
5382fb449a6SJoseph Chen if (images < 0)
5392fb449a6SJoseph Chen return images;
5402fb449a6SJoseph Chen
5412fb449a6SJoseph Chen noffset = fdt_first_subnode(fit, images);
5422fb449a6SJoseph Chen if (noffset < 0)
5432fb449a6SJoseph Chen return noffset;
5442fb449a6SJoseph Chen
5452fb449a6SJoseph Chen /* gpt must be the 1st node */
5462fb449a6SJoseph Chen name = fit_get_name(fit, noffset, NULL);
5472fb449a6SJoseph Chen if (!is_gpt(name))
5482fb449a6SJoseph Chen return 0;
5492fb449a6SJoseph Chen
5502fb449a6SJoseph Chen e = malloc(sizeof(*e));
5512fb449a6SJoseph Chen if (!e)
5522fb449a6SJoseph Chen return -ENOMEM;
5532fb449a6SJoseph Chen
5542fb449a6SJoseph Chen e->remain_tries = MAX_REMAIN_TRIES;
5552fb449a6SJoseph Chen e->buf = hdr->shared_buf;
5562fb449a6SJoseph Chen e->size = fdtdec_get_uint(fit, noffset, "data-size", -ENODATA);
5572fb449a6SJoseph Chen if (e->size == -ENODATA) {
5582fb449a6SJoseph Chen ret = -EINVAL;
5592fb449a6SJoseph Chen goto out;
5602fb449a6SJoseph Chen }
5612fb449a6SJoseph Chen
5622fb449a6SJoseph Chen strcpy(e->file_name, name);
5632fb449a6SJoseph Chen e->hash_noffset = fdt_subnode_offset(fit, noffset, "hash");
5642fb449a6SJoseph Chen if (e->hash_noffset < 0)
5652fb449a6SJoseph Chen return e->hash_noffset;
5662fb449a6SJoseph Chen
5672fb449a6SJoseph Chen printf("\n# %s:\n", e->file_name);
5682fb449a6SJoseph Chen printf(" buf: 0x%08lx\n", (ulong)e->buf);
5692fb449a6SJoseph Chen printf(" size: 0x%08x\n", e->size);
5702fb449a6SJoseph Chen printf(" remain_tries: %d\n", e->remain_tries);
5712fb449a6SJoseph Chen printf(" hash_noffset: 0x%08x\n\n", e->hash_noffset);
5722fb449a6SJoseph Chen
5732fb449a6SJoseph Chen /* download */
5742fb449a6SJoseph Chen ret = update_download_image(fit, e);
5752fb449a6SJoseph Chen if (ret) {
5762fb449a6SJoseph Chen TFTPUD_E("\"%s\" download fail, ret=%d\n",
5772fb449a6SJoseph Chen e->file_name, ret);
5782fb449a6SJoseph Chen goto out;
5792fb449a6SJoseph Chen }
5802fb449a6SJoseph Chen
5812fb449a6SJoseph Chen /* terminate gpt string */
5822fb449a6SJoseph Chen gpt_parts = (char *)e->buf;
5832fb449a6SJoseph Chen p = gpt_parts + e->size - 1;
5842fb449a6SJoseph Chen *p = '\0';
5852fb449a6SJoseph Chen
5862fb449a6SJoseph Chen /* write */
5872fb449a6SJoseph Chen printf("[TFTPUD-2]: Write gpt ...\n");
5882fb449a6SJoseph Chen printf(" %s\n\n", gpt_parts);
5892fb449a6SJoseph Chen env_set("gpt_parts", gpt_parts);
5902fb449a6SJoseph Chen ret = run_command("gpt write ${devtype} ${devnum} ${gpt_parts}", 0);
5912fb449a6SJoseph Chen if (ret) {
5922fb449a6SJoseph Chen printf("Failed to write gpt\n");
5932fb449a6SJoseph Chen ret = -EIO;
5942fb449a6SJoseph Chen goto out;
5952fb449a6SJoseph Chen }
5962fb449a6SJoseph Chen ret = run_command("gpt verify ${devtype} ${devnum} ${gpt_parts}", 0);
5972fb449a6SJoseph Chen if (ret) {
5982fb449a6SJoseph Chen printf("Failed to verify gpt\n");
5992fb449a6SJoseph Chen ret = -EIO;
6002fb449a6SJoseph Chen goto out;
6012fb449a6SJoseph Chen }
6022fb449a6SJoseph Chen printf("\n");
6032fb449a6SJoseph Chen out:
6042fb449a6SJoseph Chen free(e);
6052fb449a6SJoseph Chen
6062fb449a6SJoseph Chen return ret;
6072fb449a6SJoseph Chen }
6082fb449a6SJoseph Chen
do_tftp_full_update(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])609e83b79aaSJoseph Chen static int do_tftp_full_update(cmd_tbl_t *cmdtp, int flag,
6102fb449a6SJoseph Chen int argc, char * const argv[])
6112fb449a6SJoseph Chen {
6122fb449a6SJoseph Chen struct local_information *local = &local_info;
6132fb449a6SJoseph Chen struct update_header *hdr = &update_hdr;
6142fb449a6SJoseph Chen struct image_element *e;
6152fb449a6SJoseph Chen struct list_head *node;
6162fb449a6SJoseph Chen const char *dir_part_str;
6172fb449a6SJoseph Chen const char *part_str;
6182fb449a6SJoseph Chen const char *dir_str;
6192fb449a6SJoseph Chen char *dup_str = NULL;
6202fb449a6SJoseph Chen u32 total_success = 0;
6212fb449a6SJoseph Chen u32 total_traverse = 0;
6222fb449a6SJoseph Chen ulong start_ms;
6232fb449a6SJoseph Chen ulong total_ms;
6242fb449a6SJoseph Chen void *fit;
6252fb449a6SJoseph Chen int ret;
6262fb449a6SJoseph Chen
6272fb449a6SJoseph Chen start_ms = get_timer(0);
6282fb449a6SJoseph Chen memset(hdr, 0, sizeof(*hdr));
6292fb449a6SJoseph Chen memset(local, 0, sizeof(*local));
6302fb449a6SJoseph Chen
6312fb449a6SJoseph Chen /* only handle a single partititon ? */
6322fb449a6SJoseph Chen if (argc > 1) {
6332fb449a6SJoseph Chen dir_part_str = argv[1];
6342fb449a6SJoseph Chen part_str = strchr(dir_part_str, ':');
6352fb449a6SJoseph Chen if (part_str) {
6362fb449a6SJoseph Chen /*
6372fb449a6SJoseph Chen * eg: tftpupdate image:recovery
6382fb449a6SJoseph Chen * tftpupdate image:*
6392fb449a6SJoseph Chen * tftpupdate image:
6402fb449a6SJoseph Chen */
6412fb449a6SJoseph Chen dup_str = strdup(dir_part_str);
6422fb449a6SJoseph Chen dup_str[part_str - dir_part_str] = 0;
6432fb449a6SJoseph Chen dir_str = dup_str;
6442fb449a6SJoseph Chen part_str++;
6452fb449a6SJoseph Chen if (*part_str == '*')
6462fb449a6SJoseph Chen part_str = NULL;
6472fb449a6SJoseph Chen } else {
6482fb449a6SJoseph Chen /* eg: tftpupdate recovery */
6492fb449a6SJoseph Chen dir_str = NULL;
6502fb449a6SJoseph Chen part_str = argv[1];
6512fb449a6SJoseph Chen }
6522fb449a6SJoseph Chen } else {
6532fb449a6SJoseph Chen dir_str = NULL;
6542fb449a6SJoseph Chen part_str = NULL;
6552fb449a6SJoseph Chen }
6562fb449a6SJoseph Chen
6572fb449a6SJoseph Chen server_dir = dir_str;
6582fb449a6SJoseph Chen hdr->spec_partition = part_str;
6592fb449a6SJoseph Chen INIT_LIST_HEAD(&hdr->images);
6602fb449a6SJoseph Chen
6612fb449a6SJoseph Chen fit = update_download_hdr(hdr);
6622fb449a6SJoseph Chen if (!fit) {
6632fb449a6SJoseph Chen TFTPUD_E("download hdr fail\n");
6642fb449a6SJoseph Chen ret = -EINVAL;
6652fb449a6SJoseph Chen goto out;
6662fb449a6SJoseph Chen }
6672fb449a6SJoseph Chen
6682fb449a6SJoseph Chen ret = update_verify_hdr(fit, hdr, local);
6692fb449a6SJoseph Chen if (ret) {
6702fb449a6SJoseph Chen TFTPUD_E("verify hdr fail, ret=%d\n", ret);
6712fb449a6SJoseph Chen goto out;
6722fb449a6SJoseph Chen }
6732fb449a6SJoseph Chen
6742fb449a6SJoseph Chen /* flash gpt table early than any other partition */
6752fb449a6SJoseph Chen ret = update_write_gpt(fit, hdr);
6762fb449a6SJoseph Chen if (ret) {
6772fb449a6SJoseph Chen TFTPUD_E("write gpt fail, ret=%d\n", ret);
6782fb449a6SJoseph Chen goto out;
6792fb449a6SJoseph Chen }
6802fb449a6SJoseph Chen
6812fb449a6SJoseph Chen ret = update_populate_image(fit, hdr);
6822fb449a6SJoseph Chen if (ret) {
6832fb449a6SJoseph Chen TFTPUD_E("populate image fail, ret=%d\n", ret);
6842fb449a6SJoseph Chen goto out;
6852fb449a6SJoseph Chen }
6862fb449a6SJoseph Chen
6872fb449a6SJoseph Chen list_for_each(node, &hdr->images) {
6882fb449a6SJoseph Chen e = list_entry(node, struct image_element, node);
6892fb449a6SJoseph Chen total_traverse++;
6902fb449a6SJoseph Chen
6912fb449a6SJoseph Chen /* ignore ? */
6922fb449a6SJoseph Chen if (update_ignore_image(fit, hdr, e))
6932fb449a6SJoseph Chen continue;
6942fb449a6SJoseph Chen
6952fb449a6SJoseph Chen ret = update_download_image(fit, e);
6962fb449a6SJoseph Chen if (ret) {
6972fb449a6SJoseph Chen TFTPUD_E("\"%s\" download fail, ret=%d\n",
6982fb449a6SJoseph Chen e->file_name, ret);
6992fb449a6SJoseph Chen goto out;
7002fb449a6SJoseph Chen }
7012fb449a6SJoseph Chen
7022fb449a6SJoseph Chen ret = update_flash_image(e);
7032fb449a6SJoseph Chen if (ret) {
7042fb449a6SJoseph Chen TFTPUD_E("\"%s\" flash fail, ret=%d\n",
7052fb449a6SJoseph Chen e->file_name, ret);
7062fb449a6SJoseph Chen goto out;
7072fb449a6SJoseph Chen }
7082fb449a6SJoseph Chen
7092fb449a6SJoseph Chen total_success++;
7102fb449a6SJoseph Chen }
7112fb449a6SJoseph Chen
7122fb449a6SJoseph Chen if (total_success == 0) {
7132fb449a6SJoseph Chen if (hdr->spec_partition) {
7142fb449a6SJoseph Chen TFTPUD_E("No %s partition was found\n", hdr->spec_partition);
7152fb449a6SJoseph Chen ret = CMD_RET_FAILURE;
7162fb449a6SJoseph Chen }
7172fb449a6SJoseph Chen goto out;
7182fb449a6SJoseph Chen }
7192fb449a6SJoseph Chen
7202fb449a6SJoseph Chen /* If this is full upgrade, update local info */
7212fb449a6SJoseph Chen if (!hdr->spec_partition)
7222fb449a6SJoseph Chen update_local_info(fit, hdr);
7232fb449a6SJoseph Chen out:
7242fb449a6SJoseph Chen update_cleanup(fit, hdr);
7252fb449a6SJoseph Chen if (!ret) {
7262fb449a6SJoseph Chen total_ms = get_timer(start_ms);
7272fb449a6SJoseph Chen TFTPUD_I("tftpupdate is OK (total time: %lds, upgrade: %d/%d), "
7282fb449a6SJoseph Chen "system reboot is recommend\n",
7292fb449a6SJoseph Chen total_ms / 1000, total_success, total_traverse);
7302fb449a6SJoseph Chen }
7312fb449a6SJoseph Chen
7322fb449a6SJoseph Chen return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
7332fb449a6SJoseph Chen }
7342fb449a6SJoseph Chen
7352fb449a6SJoseph Chen U_BOOT_CMD(
736e83b79aaSJoseph Chen tftp_full_update, 2, 1, do_tftp_full_update,
7372fb449a6SJoseph Chen "Update a set of images organized with FIT via network using TFTP protocol",
7382fb449a6SJoseph Chen "[[server-dir:][partition]"
7392fb449a6SJoseph Chen );
7402fb449a6SJoseph Chen
741