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