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