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