183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+ 2c9d728ddSSimon Glass /* 3c9d728ddSSimon Glass * Copyright (C) 2017 Google, Inc 4c9d728ddSSimon Glass * Written by Simon Glass <sjg@chromium.org> 5c9d728ddSSimon Glass */ 6c9d728ddSSimon Glass 7c9d728ddSSimon Glass #include <common.h> 8c9d728ddSSimon Glass #include <environment.h> 9c9d728ddSSimon Glass 10c9d728ddSSimon Glass DECLARE_GLOBAL_DATA_PTR; 11c9d728ddSSimon Glass 127bcdf195SSiva Durga Prasad Paladugu #if defined(CONFIG_NEEDS_MANUAL_RELOC) 137bcdf195SSiva Durga Prasad Paladugu void env_fix_drivers(void) 147bcdf195SSiva Durga Prasad Paladugu { 157bcdf195SSiva Durga Prasad Paladugu struct env_driver *drv; 167bcdf195SSiva Durga Prasad Paladugu const int n_ents = ll_entry_count(struct env_driver, env_driver); 177bcdf195SSiva Durga Prasad Paladugu struct env_driver *entry; 187bcdf195SSiva Durga Prasad Paladugu 197bcdf195SSiva Durga Prasad Paladugu drv = ll_entry_start(struct env_driver, env_driver); 207bcdf195SSiva Durga Prasad Paladugu for (entry = drv; entry != drv + n_ents; entry++) { 217bcdf195SSiva Durga Prasad Paladugu if (entry->name) 227bcdf195SSiva Durga Prasad Paladugu entry->name += gd->reloc_off; 237bcdf195SSiva Durga Prasad Paladugu if (entry->load) 247bcdf195SSiva Durga Prasad Paladugu entry->load += gd->reloc_off; 257bcdf195SSiva Durga Prasad Paladugu if (entry->save) 267bcdf195SSiva Durga Prasad Paladugu entry->save += gd->reloc_off; 277bcdf195SSiva Durga Prasad Paladugu if (entry->init) 287bcdf195SSiva Durga Prasad Paladugu entry->init += gd->reloc_off; 297bcdf195SSiva Durga Prasad Paladugu } 307bcdf195SSiva Durga Prasad Paladugu } 317bcdf195SSiva Durga Prasad Paladugu #endif 327bcdf195SSiva Durga Prasad Paladugu 3352746c43SMaxime Ripard static struct env_driver *_env_driver_lookup(enum env_location loc) 34c9d728ddSSimon Glass { 35c9d728ddSSimon Glass struct env_driver *drv; 36c9d728ddSSimon Glass const int n_ents = ll_entry_count(struct env_driver, env_driver); 37c9d728ddSSimon Glass struct env_driver *entry; 38c9d728ddSSimon Glass 39c9d728ddSSimon Glass drv = ll_entry_start(struct env_driver, env_driver); 40c9d728ddSSimon Glass for (entry = drv; entry != drv + n_ents; entry++) { 41c9d728ddSSimon Glass if (loc == entry->location) 42c9d728ddSSimon Glass return entry; 43c9d728ddSSimon Glass } 44c9d728ddSSimon Glass 45c9d728ddSSimon Glass /* Not found */ 46c9d728ddSSimon Glass return NULL; 47c9d728ddSSimon Glass } 48c9d728ddSSimon Glass 497d714a24SMaxime Ripard static enum env_location env_locations[] = { 507d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_EEPROM 517d714a24SMaxime Ripard ENVL_EEPROM, 527d714a24SMaxime Ripard #endif 537d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_EXT4 547d714a24SMaxime Ripard ENVL_EXT4, 557d714a24SMaxime Ripard #endif 567d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_FAT 577d714a24SMaxime Ripard ENVL_FAT, 587d714a24SMaxime Ripard #endif 597d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_FLASH 607d714a24SMaxime Ripard ENVL_FLASH, 617d714a24SMaxime Ripard #endif 627d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_MMC 637d714a24SMaxime Ripard ENVL_MMC, 647d714a24SMaxime Ripard #endif 657d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_NAND 667d714a24SMaxime Ripard ENVL_NAND, 677d714a24SMaxime Ripard #endif 687d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_NVRAM 697d714a24SMaxime Ripard ENVL_NVRAM, 707d714a24SMaxime Ripard #endif 717d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_REMOTE 727d714a24SMaxime Ripard ENVL_REMOTE, 737d714a24SMaxime Ripard #endif 747d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_SPI_FLASH 757d714a24SMaxime Ripard ENVL_SPI_FLASH, 767d714a24SMaxime Ripard #endif 777d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_UBI 787d714a24SMaxime Ripard ENVL_UBI, 797d714a24SMaxime Ripard #endif 807d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_NOWHERE 817d714a24SMaxime Ripard ENVL_NOWHERE, 827d714a24SMaxime Ripard #endif 837d714a24SMaxime Ripard }; 847d714a24SMaxime Ripard 851d446087SMaxime Ripard static bool env_has_inited(enum env_location location) 861d446087SMaxime Ripard { 871d446087SMaxime Ripard return gd->env_has_init & BIT(location); 881d446087SMaxime Ripard } 891d446087SMaxime Ripard 901d446087SMaxime Ripard static void env_set_inited(enum env_location location) 911d446087SMaxime Ripard { 921d446087SMaxime Ripard /* 931d446087SMaxime Ripard * We're using a 32-bits bitmask stored in gd (env_has_init) 941d446087SMaxime Ripard * using the above enum value as the bit index. We need to 951d446087SMaxime Ripard * make sure that we're not overflowing it. 961d446087SMaxime Ripard */ 971d446087SMaxime Ripard BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG); 981d446087SMaxime Ripard 991d446087SMaxime Ripard gd->env_has_init |= BIT(location); 1001d446087SMaxime Ripard } 1011d446087SMaxime Ripard 1028a3a7e22SMaxime Ripard /** 1038a3a7e22SMaxime Ripard * env_get_location() - Returns the best env location for a board 1048a3a7e22SMaxime Ripard * @op: operations performed on the environment 1058a3a7e22SMaxime Ripard * @prio: priority between the multiple environments, 0 being the 1068a3a7e22SMaxime Ripard * highest priority 1078a3a7e22SMaxime Ripard * 1088a3a7e22SMaxime Ripard * This will return the preferred environment for the given priority. 10940c08a68SMaxime Ripard * This is overridable by boards if they need to. 1108a3a7e22SMaxime Ripard * 1118a3a7e22SMaxime Ripard * All implementations are free to use the operation, the priority and 1128a3a7e22SMaxime Ripard * any other data relevant to their choice, but must take into account 1138a3a7e22SMaxime Ripard * the fact that the lowest prority (0) is the most important location 1148a3a7e22SMaxime Ripard * in the system. The following locations should be returned by order 1158a3a7e22SMaxime Ripard * of descending priorities, from the highest to the lowest priority. 1168a3a7e22SMaxime Ripard * 1178a3a7e22SMaxime Ripard * Returns: 1188a3a7e22SMaxime Ripard * an enum env_location value on success, a negative error code otherwise 1198a3a7e22SMaxime Ripard */ 12040c08a68SMaxime Ripard __weak enum env_location env_get_location(enum env_operation op, int prio) 121c9d728ddSSimon Glass { 1227d714a24SMaxime Ripard if (prio >= ARRAY_SIZE(env_locations)) 1238a3a7e22SMaxime Ripard return ENVL_UNKNOWN; 1248a3a7e22SMaxime Ripard 125d30ba231SNicholas Faustini gd->env_load_prio = prio; 1267d714a24SMaxime Ripard 127d30ba231SNicholas Faustini return env_locations[prio]; 128c9d728ddSSimon Glass } 129c9d728ddSSimon Glass 1308a3a7e22SMaxime Ripard 1318a3a7e22SMaxime Ripard /** 1328a3a7e22SMaxime Ripard * env_driver_lookup() - Finds the most suited environment location 1338a3a7e22SMaxime Ripard * @op: operations performed on the environment 1348a3a7e22SMaxime Ripard * @prio: priority between the multiple environments, 0 being the 1358a3a7e22SMaxime Ripard * highest priority 1368a3a7e22SMaxime Ripard * 1378a3a7e22SMaxime Ripard * This will try to find the available environment with the highest 1388a3a7e22SMaxime Ripard * priority in the system. 1398a3a7e22SMaxime Ripard * 1408a3a7e22SMaxime Ripard * Returns: 1418a3a7e22SMaxime Ripard * NULL on error, a pointer to a struct env_driver otherwise 1428a3a7e22SMaxime Ripard */ 1438a3a7e22SMaxime Ripard static struct env_driver *env_driver_lookup(enum env_operation op, int prio) 144c9d728ddSSimon Glass { 1458a3a7e22SMaxime Ripard enum env_location loc = env_get_location(op, prio); 146c9d728ddSSimon Glass struct env_driver *drv; 147c9d728ddSSimon Glass 1488a3a7e22SMaxime Ripard if (loc == ENVL_UNKNOWN) 1498a3a7e22SMaxime Ripard return NULL; 1508a3a7e22SMaxime Ripard 15152746c43SMaxime Ripard drv = _env_driver_lookup(loc); 152c9d728ddSSimon Glass if (!drv) { 153c9d728ddSSimon Glass debug("%s: No environment driver for location %d\n", __func__, 154c9d728ddSSimon Glass loc); 155c9d728ddSSimon Glass return NULL; 156c9d728ddSSimon Glass } 157c9d728ddSSimon Glass 158c9d728ddSSimon Glass return drv; 159c9d728ddSSimon Glass } 160c9d728ddSSimon Glass 161b2cdef48SGoldschmidt Simon __weak int env_get_char_spec(int index) 162c9d728ddSSimon Glass { 163b2cdef48SGoldschmidt Simon return *(uchar *)(gd->env_addr + index); 164c9d728ddSSimon Glass } 165c9d728ddSSimon Glass 166b2cdef48SGoldschmidt Simon int env_get_char(int index) 167b2cdef48SGoldschmidt Simon { 168b2cdef48SGoldschmidt Simon if (gd->env_valid == ENV_INVALID) 169b2cdef48SGoldschmidt Simon return default_environment[index]; 170b2cdef48SGoldschmidt Simon else 171b2cdef48SGoldschmidt Simon return env_get_char_spec(index); 172c9d728ddSSimon Glass } 173c9d728ddSSimon Glass 174c9d728ddSSimon Glass int env_load(void) 175c9d728ddSSimon Glass { 1768a3a7e22SMaxime Ripard struct env_driver *drv; 1778a3a7e22SMaxime Ripard int prio; 178c9d728ddSSimon Glass 1798a3a7e22SMaxime Ripard for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) { 1808a3a7e22SMaxime Ripard int ret; 1818a3a7e22SMaxime Ripard 182c9d728ddSSimon Glass if (!drv->load) 1838a3a7e22SMaxime Ripard continue; 1848a3a7e22SMaxime Ripard 1851d446087SMaxime Ripard if (!env_has_inited(drv->location)) 1861d446087SMaxime Ripard continue; 1871d446087SMaxime Ripard 1883574ba01SMaxime Ripard printf("Loading Environment from %s... ", drv->name); 189*13bbfb4aSSam Protsenko /* 190*13bbfb4aSSam Protsenko * In error case, the error message must be printed during 191*13bbfb4aSSam Protsenko * drv->load() in some underlying API, and it must be exactly 192*13bbfb4aSSam Protsenko * one message. 193*13bbfb4aSSam Protsenko */ 194c55d8b94SSimon Glass ret = drv->load(); 195*13bbfb4aSSam Protsenko if (ret) { 196*13bbfb4aSSam Protsenko debug("Failed (%d)\n", ret); 197*13bbfb4aSSam Protsenko } else { 1983574ba01SMaxime Ripard printf("OK\n"); 1998a3a7e22SMaxime Ripard return 0; 200c9d728ddSSimon Glass } 201*13bbfb4aSSam Protsenko } 202c9d728ddSSimon Glass 203d30ba231SNicholas Faustini /* 204d30ba231SNicholas Faustini * In case of invalid environment, we set the 'default' env location 205d30ba231SNicholas Faustini * to the highest priority. In this way, next calls to env_save() 206d30ba231SNicholas Faustini * will restore the environment at the right place. 207d30ba231SNicholas Faustini */ 208d30ba231SNicholas Faustini env_get_location(ENVOP_LOAD, 0); 209d30ba231SNicholas Faustini 2108a3a7e22SMaxime Ripard return -ENODEV; 211c9d728ddSSimon Glass } 212c9d728ddSSimon Glass 213c9d728ddSSimon Glass int env_save(void) 214c9d728ddSSimon Glass { 2158a3a7e22SMaxime Ripard struct env_driver *drv; 2168a3a7e22SMaxime Ripard 217d30ba231SNicholas Faustini drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio); 218d30ba231SNicholas Faustini if (drv) { 219c9d728ddSSimon Glass int ret; 220c9d728ddSSimon Glass 221c9d728ddSSimon Glass if (!drv->save) 222d30ba231SNicholas Faustini return -ENODEV; 2239c24dfb2SMaxime Ripard 2241d446087SMaxime Ripard if (!env_has_inited(drv->location)) 225d30ba231SNicholas Faustini return -ENODEV; 2261d446087SMaxime Ripard 2279efac3c8SMaxime Ripard printf("Saving Environment to %s... ", drv->name); 228c9d728ddSSimon Glass ret = drv->save(); 2293574ba01SMaxime Ripard if (ret) 2303574ba01SMaxime Ripard printf("Failed (%d)\n", ret); 2313574ba01SMaxime Ripard else 2323574ba01SMaxime Ripard printf("OK\n"); 2333574ba01SMaxime Ripard 2348a3a7e22SMaxime Ripard if (!ret) 2358a3a7e22SMaxime Ripard return 0; 236c9d728ddSSimon Glass } 237c9d728ddSSimon Glass 2388a3a7e22SMaxime Ripard return -ENODEV; 239c9d728ddSSimon Glass } 240c9d728ddSSimon Glass 2416eeae424SSimon Glass int env_init(void) 242c9d728ddSSimon Glass { 2438a3a7e22SMaxime Ripard struct env_driver *drv; 2447938822aSSimon Glass int ret = -ENOENT; 2458a3a7e22SMaxime Ripard int prio; 246c9d728ddSSimon Glass 2478a3a7e22SMaxime Ripard for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) { 2481d446087SMaxime Ripard if (!drv->init || !(ret = drv->init())) 2491d446087SMaxime Ripard env_set_inited(drv->location); 2508a3a7e22SMaxime Ripard 2511d446087SMaxime Ripard debug("%s: Environment %s init done (ret=%d)\n", __func__, 2528a3a7e22SMaxime Ripard drv->name, ret); 2538a3a7e22SMaxime Ripard } 2548a3a7e22SMaxime Ripard 2558a3a7e22SMaxime Ripard if (!prio) 2568a3a7e22SMaxime Ripard return -ENODEV; 2578a3a7e22SMaxime Ripard 2587938822aSSimon Glass if (ret == -ENOENT) { 2597938822aSSimon Glass gd->env_addr = (ulong)&default_environment[0]; 260eeba55cbSTom Rini gd->env_valid = ENV_VALID; 2617938822aSSimon Glass 2627938822aSSimon Glass return 0; 263c9d728ddSSimon Glass } 264c9d728ddSSimon Glass 2658a3a7e22SMaxime Ripard return ret; 266c9d728ddSSimon Glass } 267