uboot/env/env.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2017 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7#include <common.h>
   8#include <env.h>
   9#include <env_internal.h>
  10#include <log.h>
  11#include <asm/global_data.h>
  12#include <linux/bitops.h>
  13#include <linux/bug.h>
  14
  15DECLARE_GLOBAL_DATA_PTR;
  16
  17#if defined(CONFIG_NEEDS_MANUAL_RELOC)
  18void env_fix_drivers(void)
  19{
  20        struct env_driver *drv;
  21        const int n_ents = ll_entry_count(struct env_driver, env_driver);
  22        struct env_driver *entry;
  23
  24        drv = ll_entry_start(struct env_driver, env_driver);
  25        for (entry = drv; entry != drv + n_ents; entry++) {
  26                if (entry->name)
  27                        entry->name += gd->reloc_off;
  28                if (entry->load)
  29                        entry->load += gd->reloc_off;
  30                if (entry->save)
  31                        entry->save += gd->reloc_off;
  32                if (entry->erase)
  33                        entry->erase += gd->reloc_off;
  34                if (entry->init)
  35                        entry->init += gd->reloc_off;
  36        }
  37}
  38#endif
  39
  40static struct env_driver *_env_driver_lookup(enum env_location loc)
  41{
  42        struct env_driver *drv;
  43        const int n_ents = ll_entry_count(struct env_driver, env_driver);
  44        struct env_driver *entry;
  45
  46        drv = ll_entry_start(struct env_driver, env_driver);
  47        for (entry = drv; entry != drv + n_ents; entry++) {
  48                if (loc == entry->location)
  49                        return entry;
  50        }
  51
  52        /* Not found */
  53        return NULL;
  54}
  55
  56static enum env_location env_locations[] = {
  57#ifdef CONFIG_ENV_IS_IN_EEPROM
  58        ENVL_EEPROM,
  59#endif
  60#ifdef CONFIG_ENV_IS_IN_EXT4
  61        ENVL_EXT4,
  62#endif
  63#ifdef CONFIG_ENV_IS_IN_FAT
  64        ENVL_FAT,
  65#endif
  66#ifdef CONFIG_ENV_IS_IN_FLASH
  67        ENVL_FLASH,
  68#endif
  69#ifdef CONFIG_ENV_IS_IN_MMC
  70        ENVL_MMC,
  71#endif
  72#ifdef CONFIG_ENV_IS_IN_NAND
  73        ENVL_NAND,
  74#endif
  75#ifdef CONFIG_ENV_IS_IN_NVRAM
  76        ENVL_NVRAM,
  77#endif
  78#ifdef CONFIG_ENV_IS_IN_REMOTE
  79        ENVL_REMOTE,
  80#endif
  81#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
  82        ENVL_SPI_FLASH,
  83#endif
  84#ifdef CONFIG_ENV_IS_IN_UBI
  85        ENVL_UBI,
  86#endif
  87#ifdef CONFIG_ENV_IS_NOWHERE
  88        ENVL_NOWHERE,
  89#endif
  90};
  91
  92static bool env_has_inited(enum env_location location)
  93{
  94        return gd->env_has_init & BIT(location);
  95}
  96
  97static void env_set_inited(enum env_location location)
  98{
  99        /*
 100         * We're using a 32-bits bitmask stored in gd (env_has_init)
 101         * using the above enum value as the bit index. We need to
 102         * make sure that we're not overflowing it.
 103         */
 104        BUILD_BUG_ON(ENVL_COUNT > BITS_PER_LONG);
 105
 106        gd->env_has_init |= BIT(location);
 107}
 108
 109/**
 110 * arch_env_get_location() - Returns the best env location for an arch
 111 * @op: operations performed on the environment
 112 * @prio: priority between the multiple environments, 0 being the
 113 *        highest priority
 114 *
 115 * This will return the preferred environment for the given priority.
 116 * This is overridable by architectures if they need to and has lower
 117 * priority than board side env_get_location() override.
 118 *
 119 * All implementations are free to use the operation, the priority and
 120 * any other data relevant to their choice, but must take into account
 121 * the fact that the lowest prority (0) is the most important location
 122 * in the system. The following locations should be returned by order
 123 * of descending priorities, from the highest to the lowest priority.
 124 *
 125 * Returns:
 126 * an enum env_location value on success, a negative error code otherwise
 127 */
 128__weak enum env_location arch_env_get_location(enum env_operation op, int prio)
 129{
 130        if (prio >= ARRAY_SIZE(env_locations))
 131                return ENVL_UNKNOWN;
 132
 133        return env_locations[prio];
 134}
 135
 136/**
 137 * env_get_location() - Returns the best env location for a board
 138 * @op: operations performed on the environment
 139 * @prio: priority between the multiple environments, 0 being the
 140 *        highest priority
 141 *
 142 * This will return the preferred environment for the given priority.
 143 * This is overridable by boards if they need to.
 144 *
 145 * All implementations are free to use the operation, the priority and
 146 * any other data relevant to their choice, but must take into account
 147 * the fact that the lowest prority (0) is the most important location
 148 * in the system. The following locations should be returned by order
 149 * of descending priorities, from the highest to the lowest priority.
 150 *
 151 * Returns:
 152 * an enum env_location value on success, a negative error code otherwise
 153 */
 154__weak enum env_location env_get_location(enum env_operation op, int prio)
 155{
 156        return arch_env_get_location(op, prio);
 157}
 158
 159/**
 160 * env_driver_lookup() - Finds the most suited environment location
 161 * @op: operations performed on the environment
 162 * @prio: priority between the multiple environments, 0 being the
 163 *        highest priority
 164 *
 165 * This will try to find the available environment with the highest
 166 * priority in the system.
 167 *
 168 * Returns:
 169 * NULL on error, a pointer to a struct env_driver otherwise
 170 */
 171static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
 172{
 173        enum env_location loc = env_get_location(op, prio);
 174        struct env_driver *drv;
 175
 176        if (loc == ENVL_UNKNOWN)
 177                return NULL;
 178
 179        drv = _env_driver_lookup(loc);
 180        if (!drv) {
 181                debug("%s: No environment driver for location %d\n", __func__,
 182                      loc);
 183                return NULL;
 184        }
 185
 186        return drv;
 187}
 188
 189int env_load(void)
 190{
 191        struct env_driver *drv;
 192        int best_prio = -1;
 193        int prio;
 194
 195        if (CONFIG_IS_ENABLED(ENV_WRITEABLE_LIST)) {
 196                /*
 197                 * When using a list of writeable variables, the baseline comes
 198                 * from the built-in default env. So load this first.
 199                 */
 200                env_set_default(NULL, 0);
 201        }
 202
 203        for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
 204                int ret;
 205
 206                if (!env_has_inited(drv->location))
 207                        continue;
 208
 209                printf("Loading Environment from %s... ", drv->name);
 210                /*
 211                 * In error case, the error message must be printed during
 212                 * drv->load() in some underlying API, and it must be exactly
 213                 * one message.
 214                 */
 215                ret = drv->load();
 216                if (!ret) {
 217                        printf("OK\n");
 218                        gd->env_load_prio = prio;
 219
 220#if !CONFIG_IS_ENABLED(ENV_APPEND)
 221                        return 0;
 222#endif
 223                } else if (ret == -ENOMSG) {
 224                        /* Handle "bad CRC" case */
 225                        if (best_prio == -1)
 226                                best_prio = prio;
 227                } else {
 228                        debug("Failed (%d)\n", ret);
 229                }
 230        }
 231
 232        /*
 233         * In case of invalid environment, we set the 'default' env location
 234         * to the best choice, i.e.:
 235         *   1. Environment location with bad CRC, if such location was found
 236         *   2. Otherwise use the location with highest priority
 237         *
 238         * This way, next calls to env_save() will restore the environment
 239         * at the right place.
 240         */
 241        if (best_prio >= 0)
 242                debug("Selecting environment with bad CRC\n");
 243        else
 244                best_prio = 0;
 245
 246        gd->env_load_prio = best_prio;
 247
 248        return -ENODEV;
 249}
 250
 251int env_reload(void)
 252{
 253        struct env_driver *drv;
 254
 255        drv = env_driver_lookup(ENVOP_LOAD, gd->env_load_prio);
 256        if (drv) {
 257                int ret;
 258
 259                printf("Loading Environment from %s... ", drv->name);
 260
 261                if (!env_has_inited(drv->location)) {
 262                        printf("not initialized\n");
 263                        return -ENODEV;
 264                }
 265
 266                ret = drv->load();
 267                if (ret)
 268                        printf("Failed (%d)\n", ret);
 269                else
 270                        printf("OK\n");
 271
 272                if (!ret)
 273                        return 0;
 274        }
 275
 276        return -ENODEV;
 277}
 278
 279int env_save(void)
 280{
 281        struct env_driver *drv;
 282
 283        drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
 284        if (drv) {
 285                int ret;
 286
 287                printf("Saving Environment to %s... ", drv->name);
 288                if (!drv->save) {
 289                        printf("not possible\n");
 290                        return -ENODEV;
 291                }
 292
 293                if (!env_has_inited(drv->location)) {
 294                        printf("not initialized\n");
 295                        return -ENODEV;
 296                }
 297
 298                ret = drv->save();
 299                if (ret)
 300                        printf("Failed (%d)\n", ret);
 301                else
 302                        printf("OK\n");
 303
 304                if (!ret)
 305                        return 0;
 306        }
 307
 308        return -ENODEV;
 309}
 310
 311int env_erase(void)
 312{
 313        struct env_driver *drv;
 314
 315        drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
 316        if (drv) {
 317                int ret;
 318
 319                if (!drv->erase) {
 320                        printf("not possible\n");
 321                        return -ENODEV;
 322                }
 323
 324                if (!env_has_inited(drv->location)) {
 325                        printf("not initialized\n");
 326                        return -ENODEV;
 327                }
 328
 329                printf("Erasing Environment on %s... ", drv->name);
 330                ret = drv->erase();
 331                if (ret)
 332                        printf("Failed (%d)\n", ret);
 333                else
 334                        printf("OK\n");
 335
 336                if (!ret)
 337                        return 0;
 338        }
 339
 340        return -ENODEV;
 341}
 342
 343int env_init(void)
 344{
 345        struct env_driver *drv;
 346        int ret = -ENOENT;
 347        int prio;
 348
 349        for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
 350                if (!drv->init || !(ret = drv->init()))
 351                        env_set_inited(drv->location);
 352                if (ret == -ENOENT)
 353                        env_set_inited(drv->location);
 354
 355                debug("%s: Environment %s init done (ret=%d)\n", __func__,
 356                      drv->name, ret);
 357
 358                if (gd->env_valid == ENV_INVALID)
 359                        ret = -ENOENT;
 360        }
 361
 362        if (!prio)
 363                return -ENODEV;
 364
 365        if (ret == -ENOENT) {
 366                gd->env_addr = (ulong)&default_environment[0];
 367                gd->env_valid = ENV_VALID;
 368
 369                return 0;
 370        }
 371
 372        return ret;
 373}
 374
 375int env_select(const char *name)
 376{
 377        struct env_driver *drv;
 378        const int n_ents = ll_entry_count(struct env_driver, env_driver);
 379        struct env_driver *entry;
 380        int prio;
 381        bool found = false;
 382
 383        printf("Select Environment on %s: ", name);
 384
 385        /* search ENV driver by name */
 386        drv = ll_entry_start(struct env_driver, env_driver);
 387        for (entry = drv; entry != drv + n_ents; entry++) {
 388                if (!strcmp(entry->name, name)) {
 389                        found = true;
 390                        break;
 391                }
 392        }
 393
 394        if (!found) {
 395                printf("driver not found\n");
 396                return -ENODEV;
 397        }
 398
 399        /* search priority by driver */
 400        for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
 401                if (entry->location == env_get_location(ENVOP_LOAD, prio)) {
 402                        /* when priority change, reset the ENV flags */
 403                        if (gd->env_load_prio != prio) {
 404                                gd->env_load_prio = prio;
 405                                gd->env_valid = ENV_INVALID;
 406                                gd->flags &= ~GD_FLG_ENV_DEFAULT;
 407                        }
 408                        printf("OK\n");
 409                        return 0;
 410                }
 411        }
 412        printf("priority not found\n");
 413
 414        return -ENODEV;
 415}
 416