X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=env%2Fenv.c;h=69848fb060807e928990cc600082cb04ff291a18;hb=10f6e4dc3a16c21f235416f975ecf2070ceb351f;hp=c186dca478d1f05ae268d604e85b458ff1ed7ec0;hpb=8a3a7e2270b39f14c1778a574bb815068d8c74d4;p=platform%2Fkernel%2Fu-boot.git diff --git a/env/env.c b/env/env.c index c186dca..69848fb 100644 --- a/env/env.c +++ b/env/env.c @@ -1,15 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2017 Google, Inc * Written by Simon Glass - * - * SPDX-License-Identifier: GPL-2.0+ */ #include -#include +#include +#include +#include +#include +#include +#include DECLARE_GLOBAL_DATA_PTR; +#if defined(CONFIG_NEEDS_MANUAL_RELOC) +void env_fix_drivers(void) +{ + struct env_driver *drv; + const int n_ents = ll_entry_count(struct env_driver, env_driver); + struct env_driver *entry; + + drv = ll_entry_start(struct env_driver, env_driver); + for (entry = drv; entry != drv + n_ents; entry++) { + if (entry->name) + entry->name += gd->reloc_off; + if (entry->load) + entry->load += gd->reloc_off; + if (entry->save) + entry->save += gd->reloc_off; + if (entry->erase) + entry->erase += gd->reloc_off; + if (entry->init) + entry->init += gd->reloc_off; + } +} +#endif + static struct env_driver *_env_driver_lookup(enum env_location loc) { struct env_driver *drv; @@ -26,13 +53,71 @@ static struct env_driver *_env_driver_lookup(enum env_location loc) return NULL; } +static enum env_location env_locations[] = { +#ifdef CONFIG_ENV_IS_IN_EEPROM + ENVL_EEPROM, +#endif +#ifdef CONFIG_ENV_IS_IN_EXT4 + ENVL_EXT4, +#endif +#ifdef CONFIG_ENV_IS_IN_FAT + ENVL_FAT, +#endif +#ifdef CONFIG_ENV_IS_IN_FLASH + ENVL_FLASH, +#endif +#ifdef CONFIG_ENV_IS_IN_MMC + ENVL_MMC, +#endif +#ifdef CONFIG_ENV_IS_IN_NAND + ENVL_NAND, +#endif +#ifdef CONFIG_ENV_IS_IN_NVRAM + ENVL_NVRAM, +#endif +#ifdef CONFIG_ENV_IS_IN_REMOTE + ENVL_REMOTE, +#endif +#ifdef CONFIG_ENV_IS_IN_SATA + ENVL_ESATA, +#endif +#ifdef CONFIG_ENV_IS_IN_SPI_FLASH + ENVL_SPI_FLASH, +#endif +#ifdef CONFIG_ENV_IS_IN_UBI + ENVL_UBI, +#endif +#ifdef CONFIG_ENV_IS_NOWHERE + ENVL_NOWHERE, +#endif +}; + +static bool env_has_inited(enum env_location location) +{ + return gd->env_has_init & BIT(location); +} + +static void env_set_inited(enum env_location location) +{ + /* + * We're using a 32-bits bitmask stored in gd (env_has_init) + * using the above enum value as the bit index. We need to + * make sure that we're not overflowing it. + */ + BUILD_BUG_ON(ENVL_COUNT > BITS_PER_LONG); + + gd->env_has_init |= BIT(location); +} + /** - * env_get_location() - Returns the best env location for a board + * arch_env_get_location() - Returns the best env location for an arch * @op: operations performed on the environment * @prio: priority between the multiple environments, 0 being the * highest priority * * This will return the preferred environment for the given priority. + * This is overridable by architectures if they need to and has lower + * priority than board side env_get_location() override. * * All implementations are free to use the operation, the priority and * any other data relevant to their choice, but must take into account @@ -43,42 +128,36 @@ static struct env_driver *_env_driver_lookup(enum env_location loc) * Returns: * an enum env_location value on success, a negative error code otherwise */ -static enum env_location env_get_location(enum env_operation op, int prio) +__weak enum env_location arch_env_get_location(enum env_operation op, int prio) { - /* - * We support a single environment, so any environment asked - * with a priority that is not zero is out of our supported - * bounds. - */ - if (prio >= 1) + if (prio >= ARRAY_SIZE(env_locations)) return ENVL_UNKNOWN; - if IS_ENABLED(CONFIG_ENV_IS_IN_EEPROM) - return ENVL_EEPROM; - else if IS_ENABLED(CONFIG_ENV_IS_IN_FAT) - return ENVL_FAT; - else if IS_ENABLED(CONFIG_ENV_IS_IN_EXT4) - return ENVL_EXT4; - else if IS_ENABLED(CONFIG_ENV_IS_IN_FLASH) - return ENVL_FLASH; - else if IS_ENABLED(CONFIG_ENV_IS_IN_MMC) - return ENVL_MMC; - else if IS_ENABLED(CONFIG_ENV_IS_IN_NAND) - return ENVL_NAND; - else if IS_ENABLED(CONFIG_ENV_IS_IN_NVRAM) - return ENVL_NVRAM; - else if IS_ENABLED(CONFIG_ENV_IS_IN_REMOTE) - return ENVL_REMOTE; - else if IS_ENABLED(CONFIG_ENV_IS_IN_SPI_FLASH) - return ENVL_SPI_FLASH; - else if IS_ENABLED(CONFIG_ENV_IS_IN_UBI) - return ENVL_UBI; - else if IS_ENABLED(CONFIG_ENV_IS_NOWHERE) - return ENVL_NOWHERE; - else - return ENVL_UNKNOWN; + return env_locations[prio]; } +/** + * env_get_location() - Returns the best env location for a board + * @op: operations performed on the environment + * @prio: priority between the multiple environments, 0 being the + * highest priority + * + * This will return the preferred environment for the given priority. + * This is overridable by boards if they need to. + * + * All implementations are free to use the operation, the priority and + * any other data relevant to their choice, but must take into account + * the fact that the lowest prority (0) is the most important location + * in the system. The following locations should be returned by order + * of descending priorities, from the highest to the lowest priority. + * + * Returns: + * an enum env_location value on success, a negative error code otherwise + */ +__weak enum env_location env_get_location(enum env_operation op, int prio) +{ + return arch_env_get_location(op, prio); +} /** * env_driver_lookup() - Finds the most suited environment location @@ -110,48 +189,83 @@ static struct env_driver *env_driver_lookup(enum env_operation op, int prio) return drv; } -int env_get_char(int index) +int env_load(void) { struct env_driver *drv; + int best_prio = -1; int prio; - if (gd->env_valid == ENV_INVALID) - return default_environment[index]; - - for (prio = 0; (drv = env_driver_lookup(ENVOP_GET_CHAR, prio)); prio++) { + for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) { int ret; - if (!drv->get_char) + if (!env_has_inited(drv->location)) continue; - ret = drv->get_char(index); - if (!ret) - return 0; + printf("Loading Environment from %s... ", drv->name); + /* + * In error case, the error message must be printed during + * drv->load() in some underlying API, and it must be exactly + * one message. + */ + ret = drv->load(); + if (!ret) { + printf("OK\n"); + gd->env_load_prio = prio; - debug("%s: Environment %s failed to load (err=%d)\n", __func__, - drv->name, ret); +#if !CONFIG_IS_ENABLED(ENV_APPEND) + return 0; +#endif + } else if (ret == -ENOMSG) { + /* Handle "bad CRC" case */ + if (best_prio == -1) + best_prio = prio; + } else { + debug("Failed (%d)\n", ret); + } } + /* + * In case of invalid environment, we set the 'default' env location + * to the best choice, i.e.: + * 1. Environment location with bad CRC, if such location was found + * 2. Otherwise use the location with highest priority + * + * This way, next calls to env_save() will restore the environment + * at the right place. + */ + if (best_prio >= 0) + debug("Selecting environment with bad CRC\n"); + else + best_prio = 0; + + gd->env_load_prio = best_prio; + return -ENODEV; } -int env_load(void) +int env_reload(void) { struct env_driver *drv; - int prio; - for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) { + drv = env_driver_lookup(ENVOP_LOAD, gd->env_load_prio); + if (drv) { int ret; - if (!drv->load) - continue; + printf("Loading Environment from %s... ", drv->name); + + if (!env_has_inited(drv->location)) { + printf("not initialized\n"); + return -ENODEV; + } ret = drv->load(); + if (ret) + printf("Failed (%d)\n", ret); + else + printf("OK\n"); + if (!ret) return 0; - - debug("%s: Environment %s failed to load (err=%d)\n", __func__, - drv->name, ret); } return -ENODEV; @@ -160,21 +274,58 @@ int env_load(void) int env_save(void) { struct env_driver *drv; - int prio; - for (prio = 0; (drv = env_driver_lookup(ENVOP_SAVE, prio)); prio++) { + drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio); + if (drv) { int ret; - if (!drv->save) - continue; + printf("Saving Environment to %s... ", drv->name); + if (!drv->save) { + printf("not possible\n"); + return -ENODEV; + } + + if (!env_has_inited(drv->location)) { + printf("not initialized\n"); + return -ENODEV; + } - printf("Saving Environment to %s...\n", drv->name); ret = drv->save(); + if (ret) + printf("Failed (%d)\n", ret); + else + printf("OK\n"); + if (!ret) return 0; + } - debug("%s: Environment %s failed to save (err=%d)\n", __func__, - drv->name, ret); + return -ENODEV; +} + +int env_erase(void) +{ + struct env_driver *drv; + + drv = env_driver_lookup(ENVOP_ERASE, gd->env_load_prio); + if (drv) { + int ret; + + if (!drv->erase) + return -ENODEV; + + if (!env_has_inited(drv->location)) + return -ENODEV; + + printf("Erasing Environment on %s... ", drv->name); + ret = drv->erase(); + if (ret) + printf("Failed (%d)\n", ret); + else + printf("OK\n"); + + if (!ret) + return 0; } return -ENODEV; @@ -187,15 +338,16 @@ int env_init(void) int prio; for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) { - if (!drv->init) - continue; + if (!drv->init || !(ret = drv->init())) + env_set_inited(drv->location); + if (ret == -ENOENT) + env_set_inited(drv->location); - ret = drv->init(); - if (!ret) - return 0; - - debug("%s: Environment %s failed to init (err=%d)\n", __func__, + debug("%s: Environment %s init done (ret=%d)\n", __func__, drv->name, ret); + + if (gd->env_valid == ENV_INVALID) + ret = -ENOENT; } if (!prio) @@ -210,3 +362,45 @@ int env_init(void) return ret; } + +int env_select(const char *name) +{ + struct env_driver *drv; + const int n_ents = ll_entry_count(struct env_driver, env_driver); + struct env_driver *entry; + int prio; + bool found = false; + + printf("Select Environment on %s: ", name); + + /* search ENV driver by name */ + drv = ll_entry_start(struct env_driver, env_driver); + for (entry = drv; entry != drv + n_ents; entry++) { + if (!strcmp(entry->name, name)) { + found = true; + break; + } + } + + if (!found) { + printf("driver not found\n"); + return -ENODEV; + } + + /* search priority by driver */ + for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) { + if (entry->location == env_get_location(ENVOP_LOAD, prio)) { + /* when priority change, reset the ENV flags */ + if (gd->env_load_prio != prio) { + gd->env_load_prio = prio; + gd->env_valid = ENV_INVALID; + gd->flags &= ~GD_FLG_ENV_DEFAULT; + } + printf("OK\n"); + return 0; + } + } + printf("priority not found\n"); + + return -ENODEV; +}