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_SATA
  82        ENVL_ESATA,
  83#endif
  84#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
  85        ENVL_SPI_FLASH,
  86#endif
  87#ifdef CONFIG_ENV_IS_IN_UBI
  88        ENVL_UBI,
  89#endif
  90#ifdef CONFIG_ENV_IS_NOWHERE
  91        ENVL_NOWHERE,
  92#endif
  93};
  94
  95static bool env_has_inited(enum env_location location)
  96{
  97        return gd->env_has_init & BIT(location);
  98}
  99
 100static void env_set_inited(enum env_location location)
 101{
 102        /*
 103         * We're using a 32-bits bitmask stored in gd (env_has_init)
 104         * using the above enum value as the bit index. We need to
 105         * make sure that we're not overflowing it.
 106         */
 107        BUILD_BUG_ON(ENVL_COUNT > BITS_PER_LONG);
 108
 109        gd->env_has_init |= BIT(location);
 110}
 111
 112/**
 113 * env_get_location() - Returns the best env location for a board
 114 * @op: operations performed on the environment
 115 * @prio: priority between the multiple environments, 0 being the
 116 *        highest priority
 117 *
 118 * This will return the preferred environment for the given priority.
 119 * This is overridable by boards if they need to.
 120 *
 121 * All implementations are free to use the operation, the priority and
 122 * any other data relevant to their choice, but must take into account
 123 * the fact that the lowest prority (0) is the most important location
 124 * in the system. The following locations should be returned by order
 125 * of descending priorities, from the highest to the lowest priority.
 126 *
 127 * Returns:
 128 * an enum env_location value on success, a negative error code otherwise
 129 */
 130__weak enum env_location env_get_location(enum env_operation op, int prio)
 131{
 132        if (prio >= ARRAY_SIZE(env_locations))
 133                return ENVL_UNKNOWN;
 134
 135        return env_locations[prio];
 136}
 137
 138
 139/**
 140 * env_driver_lookup() - Finds the most suited environment location
 141 * @op: operations performed on the environment
 142 * @prio: priority between the multiple environments, 0 being the
 143 *        highest priority
 144 *
 145 * This will try to find the available environment with the highest
 146 * priority in the system.
 147 *
 148 * Returns:
 149 * NULL on error, a pointer to a struct env_driver otherwise
 150 */
 151static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
 152{
 153        enum env_location loc = env_get_location(op, prio);
 154        struct env_driver *drv;
 155
 156        if (loc == ENVL_UNKNOWN)
 157                return NULL;
 158
 159        drv = _env_driver_lookup(loc);
 160        if (!drv) {
 161                debug("%s: No environment driver for location %d\n", __func__,
 162                      loc);
 163                return NULL;
 164        }
 165
 166        return drv;
 167}
 168
 169__weak int env_get_char_spec(int index)
 170{
 171        return *(uchar *)(gd->env_addr + index);
 172}
 173
 174int env_get_char(int index)
 175{
 176        if (gd->env_valid == ENV_INVALID)
 177                return default_environment[index];
 178        else
 179                return env_get_char_spec(index);
 180}
 181
 182int env_load(void)
 183{
 184        struct env_driver *drv;
 185        int best_prio = -1;
 186        int prio;
 187
 188        for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
 189                int ret;
 190
 191                if (!env_has_inited(drv->location))
 192                        continue;
 193
 194                printf("Loading Environment from %s... ", drv->name);
 195                /*
 196                 * In error case, the error message must be printed during
 197                 * drv->load() in some underlying API, and it must be exactly
 198                 * one message.
 199                 */
 200                ret = drv->load();
 201                if (!ret) {
 202                        printf("OK\n");
 203                        gd->env_load_prio = prio;
 204
 205#if !CONFIG_IS_ENABLED(ENV_APPEND)
 206                        return 0;
 207#endif
 208                } else if (ret == -ENOMSG) {
 209                        /* Handle "bad CRC" case */
 210                        if (best_prio == -1)
 211                                best_prio = prio;
 212                } else {
 213                        debug("Failed (%d)\n", ret);
 214                }
 215        }
 216
 217        /*
 218         * In case of invalid environment, we set the 'default' env location
 219         * to the best choice, i.e.:
 220         *   1. Environment location with bad CRC, if such location was found
 221         *   2. Otherwise use the location with highest priority
 222         *
 223         * This way, next calls to env_save() will restore the environment
 224         * at the right place.
 225         */
 226        if (best_prio >= 0)
 227                debug("Selecting environment with bad CRC\n");
 228        else
 229                best_prio = 0;
 230
 231        gd->env_load_prio = best_prio;
 232
 233        return -ENODEV;
 234}
 235
 236int env_reload(void)
 237{
 238        struct env_driver *drv;
 239
 240        drv = env_driver_lookup(ENVOP_LOAD, gd->env_load_prio);
 241        if (drv) {
 242                int ret;
 243
 244                printf("Loading Environment from %s... ", drv->name);
 245
 246                if (!env_has_inited(drv->location)) {
 247                        printf("not initialized\n");
 248                        return -ENODEV;
 249                }
 250
 251                ret = drv->load();
 252                if (ret)
 253                        printf("Failed (%d)\n", ret);
 254                else
 255                        printf("OK\n");
 256
 257                if (!ret)
 258                        return 0;
 259        }
 260
 261        return -ENODEV;
 262}
 263
 264int env_save(void)
 265{
 266        struct env_driver *drv;
 267
 268        drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
 269        if (drv) {
 270                int ret;
 271
 272                printf("Saving Environment to %s... ", drv->name);
 273                if (!drv->save) {
 274                        printf("not possible\n");
 275                        return -ENODEV;
 276                }
 277
 278                if (!env_has_inited(drv->location)) {
 279                        printf("not initialized\n");
 280                        return -ENODEV;
 281                }
 282
 283                ret = drv->save();
 284                if (ret)
 285                        printf("Failed (%d)\n", ret);
 286                else
 287                        printf("OK\n");
 288
 289                if (!ret)
 290                        return 0;
 291        }
 292
 293        return -ENODEV;
 294}
 295
 296int env_erase(void)
 297{
 298        struct env_driver *drv;
 299
 300        drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
 301        if (drv) {
 302                int ret;
 303
 304                if (!drv->erase)
 305                        return -ENODEV;
 306
 307                if (!env_has_inited(drv->location))
 308                        return -ENODEV;
 309
 310                printf("Erasing Environment on %s... ", drv->name);
 311                ret = drv->erase();
 312                if (ret)
 313                        printf("Failed (%d)\n", ret);
 314                else
 315                        printf("OK\n");
 316
 317                if (!ret)
 318                        return 0;
 319        }
 320
 321        return -ENODEV;
 322}
 323
 324int env_init(void)
 325{
 326        struct env_driver *drv;
 327        int ret = -ENOENT;
 328        int prio;
 329
 330        for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
 331                if (!drv->init || !(ret = drv->init()))
 332                        env_set_inited(drv->location);
 333                if (ret == -ENOENT)
 334                        env_set_inited(drv->location);
 335
 336                debug("%s: Environment %s init done (ret=%d)\n", __func__,
 337                      drv->name, ret);
 338
 339                if (gd->env_valid == ENV_INVALID)
 340                        ret = -ENOENT;
 341        }
 342
 343        if (!prio)
 344                return -ENODEV;
 345
 346        if (ret == -ENOENT) {
 347                gd->env_addr = (ulong)&default_environment[0];
 348                gd->env_valid = ENV_VALID;
 349
 350                return 0;
 351        }
 352
 353        return ret;
 354}
 355
 356int env_select(const char *name)
 357{
 358        struct env_driver *drv;
 359        const int n_ents = ll_entry_count(struct env_driver, env_driver);
 360        struct env_driver *entry;
 361        int prio;
 362        bool found = false;
 363
 364        printf("Select Environment on %s: ", name);
 365
 366        /* search ENV driver by name */
 367        drv = ll_entry_start(struct env_driver, env_driver);
 368        for (entry = drv; entry != drv + n_ents; entry++) {
 369                if (!strcmp(entry->name, name)) {
 370                        found = true;
 371                        break;
 372                }
 373        }
 374
 375        if (!found) {
 376                printf("driver not found\n");
 377                return -ENODEV;
 378        }
 379
 380        /* search priority by driver */
 381        for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
 382                if (entry->location == env_get_location(ENVOP_LOAD, prio)) {
 383                        /* when priority change, reset the ENV flags */
 384                        if (gd->env_load_prio != prio) {
 385                                gd->env_load_prio = prio;
 386                                gd->env_valid = ENV_INVALID;
 387                                gd->flags &= ~GD_FLG_ENV_DEFAULT;
 388                        }
 389                        printf("OK\n");
 390                        return 0;
 391                }
 392        }
 393        printf("priority not found\n");
 394
 395        return -ENODEV;
 396}
 397