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
  11DECLARE_GLOBAL_DATA_PTR;
  12
  13#if defined(CONFIG_NEEDS_MANUAL_RELOC)
  14void env_fix_drivers(void)
  15{
  16        struct env_driver *drv;
  17        const int n_ents = ll_entry_count(struct env_driver, env_driver);
  18        struct env_driver *entry;
  19
  20        drv = ll_entry_start(struct env_driver, env_driver);
  21        for (entry = drv; entry != drv + n_ents; entry++) {
  22                if (entry->name)
  23                        entry->name += gd->reloc_off;
  24                if (entry->load)
  25                        entry->load += gd->reloc_off;
  26                if (entry->save)
  27                        entry->save += gd->reloc_off;
  28                if (entry->erase)
  29                        entry->erase += gd->reloc_off;
  30                if (entry->init)
  31                        entry->init += gd->reloc_off;
  32        }
  33}
  34#endif
  35
  36static struct env_driver *_env_driver_lookup(enum env_location loc)
  37{
  38        struct env_driver *drv;
  39        const int n_ents = ll_entry_count(struct env_driver, env_driver);
  40        struct env_driver *entry;
  41
  42        drv = ll_entry_start(struct env_driver, env_driver);
  43        for (entry = drv; entry != drv + n_ents; entry++) {
  44                if (loc == entry->location)
  45                        return entry;
  46        }
  47
  48        /* Not found */
  49        return NULL;
  50}
  51
  52static enum env_location env_locations[] = {
  53#ifdef CONFIG_ENV_IS_IN_EEPROM
  54        ENVL_EEPROM,
  55#endif
  56#ifdef CONFIG_ENV_IS_IN_EXT4
  57        ENVL_EXT4,
  58#endif
  59#ifdef CONFIG_ENV_IS_IN_FAT
  60        ENVL_FAT,
  61#endif
  62#ifdef CONFIG_ENV_IS_IN_FLASH
  63        ENVL_FLASH,
  64#endif
  65#ifdef CONFIG_ENV_IS_IN_MMC
  66        ENVL_MMC,
  67#endif
  68#ifdef CONFIG_ENV_IS_IN_NAND
  69        ENVL_NAND,
  70#endif
  71#ifdef CONFIG_ENV_IS_IN_NVRAM
  72        ENVL_NVRAM,
  73#endif
  74#ifdef CONFIG_ENV_IS_IN_REMOTE
  75        ENVL_REMOTE,
  76#endif
  77#ifdef CONFIG_ENV_IS_IN_SATA
  78        ENVL_ESATA,
  79#endif
  80#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
  81        ENVL_SPI_FLASH,
  82#endif
  83#ifdef CONFIG_ENV_IS_IN_UBI
  84        ENVL_UBI,
  85#endif
  86#ifdef CONFIG_ENV_IS_NOWHERE
  87        ENVL_NOWHERE,
  88#endif
  89};
  90
  91static bool env_has_inited(enum env_location location)
  92{
  93        return gd->env_has_init & BIT(location);
  94}
  95
  96static void env_set_inited(enum env_location location)
  97{
  98        /*
  99         * We're using a 32-bits bitmask stored in gd (env_has_init)
 100         * using the above enum value as the bit index. We need to
 101         * make sure that we're not overflowing it.
 102         */
 103        BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
 104
 105        gd->env_has_init |= BIT(location);
 106}
 107
 108/**
 109 * env_get_location() - Returns the best env location for a board
 110 * @op: operations performed on the environment
 111 * @prio: priority between the multiple environments, 0 being the
 112 *        highest priority
 113 *
 114 * This will return the preferred environment for the given priority.
 115 * This is overridable by boards if they need to.
 116 *
 117 * All implementations are free to use the operation, the priority and
 118 * any other data relevant to their choice, but must take into account
 119 * the fact that the lowest prority (0) is the most important location
 120 * in the system. The following locations should be returned by order
 121 * of descending priorities, from the highest to the lowest priority.
 122 *
 123 * Returns:
 124 * an enum env_location value on success, a negative error code otherwise
 125 */
 126__weak enum env_location env_get_location(enum env_operation op, int prio)
 127{
 128        if (prio >= ARRAY_SIZE(env_locations))
 129                return ENVL_UNKNOWN;
 130
 131        gd->env_load_prio = prio;
 132
 133        return env_locations[prio];
 134}
 135
 136
 137/**
 138 * env_driver_lookup() - Finds the most suited environment location
 139 * @op: operations performed on the environment
 140 * @prio: priority between the multiple environments, 0 being the
 141 *        highest priority
 142 *
 143 * This will try to find the available environment with the highest
 144 * priority in the system.
 145 *
 146 * Returns:
 147 * NULL on error, a pointer to a struct env_driver otherwise
 148 */
 149static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
 150{
 151        enum env_location loc = env_get_location(op, prio);
 152        struct env_driver *drv;
 153
 154        if (loc == ENVL_UNKNOWN)
 155                return NULL;
 156
 157        drv = _env_driver_lookup(loc);
 158        if (!drv) {
 159                debug("%s: No environment driver for location %d\n", __func__,
 160                      loc);
 161                return NULL;
 162        }
 163
 164        return drv;
 165}
 166
 167__weak int env_get_char_spec(int index)
 168{
 169        return *(uchar *)(gd->env_addr + index);
 170}
 171
 172int env_get_char(int index)
 173{
 174        if (gd->env_valid == ENV_INVALID)
 175                return default_environment[index];
 176        else
 177                return env_get_char_spec(index);
 178}
 179
 180int env_load(void)
 181{
 182        struct env_driver *drv;
 183        int best_prio = -1;
 184        int prio;
 185
 186        for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
 187                int ret;
 188
 189                if (!drv->load)
 190                        continue;
 191
 192                if (!env_has_inited(drv->location))
 193                        continue;
 194
 195                printf("Loading Environment from %s... ", drv->name);
 196                /*
 197                 * In error case, the error message must be printed during
 198                 * drv->load() in some underlying API, and it must be exactly
 199                 * one message.
 200                 */
 201                ret = drv->load();
 202                if (!ret) {
 203                        printf("OK\n");
 204                        return 0;
 205                } else if (ret == -ENOMSG) {
 206                        /* Handle "bad CRC" case */
 207                        if (best_prio == -1)
 208                                best_prio = prio;
 209                } else {
 210                        debug("Failed (%d)\n", ret);
 211                }
 212        }
 213
 214        /*
 215         * In case of invalid environment, we set the 'default' env location
 216         * to the best choice, i.e.:
 217         *   1. Environment location with bad CRC, if such location was found
 218         *   2. Otherwise use the location with highest priority
 219         *
 220         * This way, next calls to env_save() will restore the environment
 221         * at the right place.
 222         */
 223        if (best_prio >= 0)
 224                debug("Selecting environment with bad CRC\n");
 225        else
 226                best_prio = 0;
 227        env_get_location(ENVOP_LOAD, best_prio);
 228
 229        return -ENODEV;
 230}
 231
 232int env_save(void)
 233{
 234        struct env_driver *drv;
 235
 236        drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
 237        if (drv) {
 238                int ret;
 239
 240                if (!drv->save)
 241                        return -ENODEV;
 242
 243                if (!env_has_inited(drv->location))
 244                        return -ENODEV;
 245
 246                printf("Saving Environment to %s... ", drv->name);
 247                ret = drv->save();
 248                if (ret)
 249                        printf("Failed (%d)\n", ret);
 250                else
 251                        printf("OK\n");
 252
 253                if (!ret)
 254                        return 0;
 255        }
 256
 257        return -ENODEV;
 258}
 259
 260int env_erase(void)
 261{
 262        struct env_driver *drv;
 263
 264        drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio);
 265        if (drv) {
 266                int ret;
 267
 268                if (!drv->erase)
 269                        return -ENODEV;
 270
 271                if (!env_has_inited(drv->location))
 272                        return -ENODEV;
 273
 274                printf("Erasing Environment on %s... ", drv->name);
 275                ret = drv->erase();
 276                if (ret)
 277                        printf("Failed (%d)\n", ret);
 278                else
 279                        printf("OK\n");
 280
 281                if (!ret)
 282                        return 0;
 283        }
 284
 285        return -ENODEV;
 286}
 287
 288int env_init(void)
 289{
 290        struct env_driver *drv;
 291        int ret = -ENOENT;
 292        int prio;
 293
 294        for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
 295                if (!drv->init || !(ret = drv->init()))
 296                        env_set_inited(drv->location);
 297
 298                debug("%s: Environment %s init done (ret=%d)\n", __func__,
 299                      drv->name, ret);
 300        }
 301
 302        if (!prio)
 303                return -ENODEV;
 304
 305        if (ret == -ENOENT) {
 306                gd->env_addr = (ulong)&default_environment[0];
 307                gd->env_valid = ENV_VALID;
 308
 309                return 0;
 310        }
 311
 312        return ret;
 313}
 314