uboot/common/update.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2008 Semihalf
   4 *
   5 * Written by: Rafal Czubak <rcz@semihalf.com>
   6 *             Bartlomiej Sieka <tur@semihalf.com>
   7 */
   8
   9#include <common.h>
  10#include <cpu_func.h>
  11#include <image.h>
  12
  13#if !(defined(CONFIG_FIT) && defined(CONFIG_OF_LIBFDT))
  14#error "CONFIG_FIT and CONFIG_OF_LIBFDT are required for auto-update feature"
  15#endif
  16
  17#if defined(CONFIG_UPDATE_TFTP) && !defined(CONFIG_MTD_NOR_FLASH)
  18#error "CONFIG_UPDATE_TFTP and !CONFIG_MTD_NOR_FLASH needed for legacy behaviour"
  19#endif
  20
  21#include <command.h>
  22#include <env.h>
  23#include <flash.h>
  24#include <net.h>
  25#include <net/tftp.h>
  26#include <malloc.h>
  27#include <mapmem.h>
  28#include <dfu.h>
  29#include <errno.h>
  30#include <mtd/cfi_flash.h>
  31
  32#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
  33/* env variable holding the location of the update file */
  34#define UPDATE_FILE_ENV         "updatefile"
  35
  36/* set configuration defaults if needed */
  37#ifndef CONFIG_UPDATE_LOAD_ADDR
  38#define CONFIG_UPDATE_LOAD_ADDR 0x100000
  39#endif
  40
  41#ifndef CONFIG_UPDATE_TFTP_MSEC_MAX
  42#define CONFIG_UPDATE_TFTP_MSEC_MAX     100
  43#endif
  44
  45#ifndef CONFIG_UPDATE_TFTP_CNT_MAX
  46#define CONFIG_UPDATE_TFTP_CNT_MAX      0
  47#endif
  48
  49extern ulong tftp_timeout_ms;
  50extern int tftp_timeout_count_max;
  51#ifdef CONFIG_MTD_NOR_FLASH
  52extern flash_info_t flash_info[];
  53static uchar *saved_prot_info;
  54#endif
  55static int update_load(char *filename, ulong msec_max, int cnt_max, ulong addr)
  56{
  57        int size, rv;
  58        ulong saved_timeout_msecs;
  59        int saved_timeout_count;
  60        char *saved_netretry, *saved_bootfile;
  61
  62        rv = 0;
  63        /* save used globals and env variable */
  64        saved_timeout_msecs = tftp_timeout_ms;
  65        saved_timeout_count = tftp_timeout_count_max;
  66        saved_netretry = strdup(env_get("netretry"));
  67        saved_bootfile = strdup(net_boot_file_name);
  68
  69        /* set timeouts for auto-update */
  70        tftp_timeout_ms = msec_max;
  71        tftp_timeout_count_max = cnt_max;
  72
  73        /* we don't want to retry the connection if errors occur */
  74        env_set("netretry", "no");
  75
  76        /* download the update file */
  77        image_load_addr = addr;
  78        copy_filename(net_boot_file_name, filename, sizeof(net_boot_file_name));
  79        size = net_loop(TFTPGET);
  80
  81        if (size < 0)
  82                rv = 1;
  83        else if (size > 0)
  84                flush_cache(addr, size);
  85
  86        /* restore changed globals and env variable */
  87        tftp_timeout_ms = saved_timeout_msecs;
  88        tftp_timeout_count_max = saved_timeout_count;
  89
  90        env_set("netretry", saved_netretry);
  91        if (saved_netretry != NULL)
  92                free(saved_netretry);
  93
  94        if (saved_bootfile != NULL) {
  95                copy_filename(net_boot_file_name, saved_bootfile,
  96                              sizeof(net_boot_file_name));
  97                free(saved_bootfile);
  98        }
  99
 100        return rv;
 101}
 102
 103#ifdef CONFIG_MTD_NOR_FLASH
 104static int update_flash_protect(int prot, ulong addr_first, ulong addr_last)
 105{
 106        uchar *sp_info_ptr;
 107        ulong s;
 108        int i, bank, cnt;
 109        flash_info_t *info;
 110
 111        sp_info_ptr = NULL;
 112
 113        if (prot == 0) {
 114                saved_prot_info =
 115                        calloc(CONFIG_SYS_MAX_FLASH_BANKS * CONFIG_SYS_MAX_FLASH_SECT, 1);
 116                if (!saved_prot_info)
 117                        return 1;
 118        }
 119
 120        for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank) {
 121                cnt = 0;
 122                info = &flash_info[bank];
 123
 124                /* Nothing to do if the bank doesn't exist */
 125                if (info->sector_count == 0)
 126                        return 0;
 127
 128                /* Point to current bank protection information */
 129                sp_info_ptr = saved_prot_info + (bank * CONFIG_SYS_MAX_FLASH_SECT);
 130
 131                /*
 132                 * Adjust addr_first or addr_last if we are on bank boundary.
 133                 * Address space between banks must be continuous for other
 134                 * flash functions (like flash_sect_erase or flash_write) to
 135                 * succeed. Banks must also be numbered in correct order,
 136                 * according to increasing addresses.
 137                 */
 138                if (addr_last > info->start[0] + info->size - 1)
 139                        addr_last = info->start[0] + info->size - 1;
 140                if (addr_first < info->start[0])
 141                        addr_first = info->start[0];
 142
 143                for (i = 0; i < info->sector_count; i++) {
 144                        /* Save current information about protected sectors */
 145                        if (prot == 0) {
 146                                s = info->start[i];
 147                                if ((s >= addr_first) && (s <= addr_last))
 148                                        sp_info_ptr[i] = info->protect[i];
 149
 150                        }
 151
 152                        /* Protect/unprotect sectors */
 153                        if (sp_info_ptr[i] == 1) {
 154#if defined(CONFIG_SYS_FLASH_PROTECTION)
 155                                if (flash_real_protect(info, i, prot))
 156                                        return 1;
 157#else
 158                                info->protect[i] = prot;
 159#endif
 160                                cnt++;
 161                        }
 162                }
 163
 164                if (cnt) {
 165                        printf("%sProtected %d sectors\n",
 166                                                prot ? "": "Un-", cnt);
 167                }
 168        }
 169
 170        if((prot == 1) && saved_prot_info)
 171                free(saved_prot_info);
 172
 173        return 0;
 174}
 175#endif
 176
 177static int update_flash(ulong addr_source, ulong addr_first, ulong size)
 178{
 179#ifdef CONFIG_MTD_NOR_FLASH
 180        ulong addr_last = addr_first + size - 1;
 181
 182        /* round last address to the sector boundary */
 183        if (flash_sect_roundb(&addr_last) > 0)
 184                return 1;
 185
 186        if (addr_first >= addr_last) {
 187                printf("Error: end address exceeds addressing space\n");
 188                return 1;
 189        }
 190
 191        /* remove protection on processed sectors */
 192        if (update_flash_protect(0, addr_first, addr_last) > 0) {
 193                printf("Error: could not unprotect flash sectors\n");
 194                return 1;
 195        }
 196
 197        printf("Erasing 0x%08lx - 0x%08lx", addr_first, addr_last);
 198        if (flash_sect_erase(addr_first, addr_last) > 0) {
 199                printf("Error: could not erase flash\n");
 200                return 1;
 201        }
 202
 203        printf("Copying to flash...");
 204        if (flash_write((char *)addr_source, addr_first, size) > 0) {
 205                printf("Error: could not copy to flash\n");
 206                return 1;
 207        }
 208        printf("done\n");
 209
 210        /* enable protection on processed sectors */
 211        if (update_flash_protect(1, addr_first, addr_last) > 0) {
 212                printf("Error: could not protect flash sectors\n");
 213                return 1;
 214        }
 215#endif
 216        return 0;
 217}
 218#endif /* CONFIG_DFU_TFTP || CONFIG_UPDATE_TFTP */
 219
 220static int update_fit_getparams(const void *fit, int noffset, ulong *addr,
 221                                                ulong *fladdr, ulong *size)
 222{
 223        const void *data;
 224
 225        if (fit_image_get_data(fit, noffset, &data, (size_t *)size))
 226                return 1;
 227
 228        if (fit_image_get_load(fit, noffset, (ulong *)fladdr))
 229                return 1;
 230
 231        *addr = (ulong)data;
 232
 233        return 0;
 234}
 235
 236#if defined(CONFIG_DFU_TFTP) || defined(CONFIG_UPDATE_TFTP)
 237int update_tftp(ulong addr, char *interface, char *devstring)
 238{
 239        char *filename, *env_addr, *fit_image_name;
 240        ulong update_addr, update_fladdr, update_size;
 241        int images_noffset, ndepth, noffset;
 242        bool update_tftp_dfu;
 243        int ret = 0;
 244        void *fit;
 245
 246        if (interface == NULL && devstring == NULL) {
 247                update_tftp_dfu = false;
 248        } else if (interface && devstring) {
 249                update_tftp_dfu = true;
 250        } else {
 251                pr_err("Interface: %s and devstring: %s not supported!\n",
 252                      interface, devstring);
 253                return -EINVAL;
 254        }
 255
 256        /* use already present image */
 257        if (addr)
 258                goto got_update_file;
 259
 260        printf("Auto-update from TFTP: ");
 261
 262        /* get the file name of the update file */
 263        filename = env_get(UPDATE_FILE_ENV);
 264        if (filename == NULL) {
 265                printf("failed, env. variable '%s' not found\n",
 266                                                        UPDATE_FILE_ENV);
 267                return 1;
 268        }
 269
 270        printf("trying update file '%s'\n", filename);
 271
 272        /* get load address of downloaded update file */
 273        env_addr = env_get("loadaddr");
 274        if (env_addr)
 275                addr = hextoul(env_addr, NULL);
 276        else
 277                addr = CONFIG_UPDATE_LOAD_ADDR;
 278
 279
 280        if (update_load(filename, CONFIG_UPDATE_TFTP_MSEC_MAX,
 281                                        CONFIG_UPDATE_TFTP_CNT_MAX, addr)) {
 282                printf("Can't load update file, aborting auto-update\n");
 283                return 1;
 284        }
 285
 286got_update_file:
 287        fit = map_sysmem(addr, 0);
 288
 289        if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
 290                printf("Bad FIT format of the update file, aborting "
 291                                                        "auto-update\n");
 292                return 1;
 293        }
 294
 295        /* process updates */
 296        images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
 297
 298        ndepth = 0;
 299        noffset = fdt_next_node(fit, images_noffset, &ndepth);
 300        while (noffset >= 0 && ndepth > 0) {
 301                if (ndepth != 1)
 302                        goto next_node;
 303
 304                fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
 305                printf("Processing update '%s' :", fit_image_name);
 306
 307                if (!fit_image_verify(fit, noffset)) {
 308                        printf("Error: invalid update hash, aborting\n");
 309                        ret = 1;
 310                        goto next_node;
 311                }
 312
 313                printf("\n");
 314                if (update_fit_getparams(fit, noffset, &update_addr,
 315                                        &update_fladdr, &update_size)) {
 316                        printf("Error: can't get update parameters, aborting\n");
 317                        ret = 1;
 318                        goto next_node;
 319                }
 320
 321                if (!update_tftp_dfu) {
 322                        if (update_flash(update_addr, update_fladdr,
 323                                         update_size)) {
 324                                printf("Error: can't flash update, aborting\n");
 325                                ret = 1;
 326                                goto next_node;
 327                        }
 328                } else if (fit_image_check_type(fit, noffset,
 329                                                IH_TYPE_FIRMWARE)) {
 330                        ret = dfu_write_by_name(fit_image_name,
 331                                                (void *)update_addr,
 332                                                update_size, interface,
 333                                                devstring);
 334                        if (ret)
 335                                return ret;
 336                }
 337next_node:
 338                noffset = fdt_next_node(fit, noffset, &ndepth);
 339        }
 340
 341        return ret;
 342}
 343#endif /* CONFIG_DFU_UPDATE || CONFIG_UPDATE_TFTP */
 344
 345#ifdef CONFIG_UPDATE_FIT
 346/**
 347 * fit_update - update storage with FIT image
 348 * @fit:        Pointer to FIT image
 349 *
 350 * Update firmware on storage using FIT image as input.
 351 * The storage area to be update will be identified by the name
 352 * in FIT and matching it to "dfu_alt_info" variable.
 353 *
 354 * Return:      0 - on success, non-zero - otherwise
 355 */
 356int fit_update(const void *fit)
 357{
 358        char *fit_image_name;
 359        ulong update_addr, update_fladdr, update_size;
 360        int images_noffset, ndepth, noffset;
 361        int ret = 0;
 362
 363        if (!fit)
 364                return -EINVAL;
 365
 366        if (fit_check_format((void *)fit, IMAGE_SIZE_INVAL)) {
 367                printf("Bad FIT format of the update file, aborting auto-update\n");
 368                return -EINVAL;
 369        }
 370
 371        /* process updates */
 372        images_noffset = fdt_path_offset(fit, FIT_IMAGES_PATH);
 373
 374        ndepth = 0;
 375        noffset = fdt_next_node(fit, images_noffset, &ndepth);
 376        while (noffset >= 0 && ndepth > 0) {
 377                if (ndepth != 1)
 378                        goto next_node;
 379
 380                fit_image_name = (char *)fit_get_name(fit, noffset, NULL);
 381                printf("Processing update '%s' :", fit_image_name);
 382
 383                if (!fit_image_verify(fit, noffset)) {
 384                        printf("Error: invalid update hash, aborting\n");
 385                        ret = 1;
 386                        goto next_node;
 387                }
 388
 389                printf("\n");
 390                if (update_fit_getparams(fit, noffset, &update_addr,
 391                                         &update_fladdr, &update_size)) {
 392                        printf("Error: can't get update parameters, aborting\n");
 393                        ret = 1;
 394                        goto next_node;
 395                }
 396
 397                if (fit_image_check_type(fit, noffset, IH_TYPE_FIRMWARE)) {
 398                        ret = dfu_write_by_name(fit_image_name,
 399                                                (void *)update_addr,
 400                                                update_size, NULL, NULL);
 401                        if (ret)
 402                                return ret;
 403                }
 404next_node:
 405                noffset = fdt_next_node(fit, noffset, &ndepth);
 406        }
 407
 408        return ret;
 409}
 410#endif /* CONFIG_UPDATE_FIT */
 411