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 prio; 181 182 for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) { 183 int ret; 184 185 if (!drv->load) 186 continue; 187 188 if (!env_has_inited(drv->location)) 189 continue; 190 191 printf("Loading Environment from %s... ", drv->name); 192 /* 193 * In error case, the error message must be printed during 194 * drv->load() in some underlying API, and it must be exactly 195 * one message. 196 */ 197 ret = drv->load(); 198 if (ret) { 199 debug("Failed (%d)\n", ret); 200 } else { 201 printf("OK\n"); 202 return 0; 203 } 204 } 205 206 /* 207 * In case of invalid environment, we set the 'default' env location 208 * to the highest priority. In this way, next calls to env_save() 209 * will restore the environment at the right place. 210 */ 211 env_get_location(ENVOP_LOAD, 0); 212 213 return -ENODEV; 214 } 215 216 int env_save(void) 217 { 218 struct env_driver *drv; 219 220 drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio); 221 if (drv) { 222 int ret; 223 224 if (!drv->save) 225 return -ENODEV; 226 227 if (!env_has_inited(drv->location)) 228 return -ENODEV; 229 230 printf("Saving Environment to %s... ", drv->name); 231 ret = drv->save(); 232 if (ret) 233 printf("Failed (%d)\n", ret); 234 else 235 printf("OK\n"); 236 237 if (!ret) 238 return 0; 239 } 240 241 return -ENODEV; 242 } 243 244 int env_init(void) 245 { 246 struct env_driver *drv; 247 int ret = -ENOENT; 248 int prio; 249 250 for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) { 251 if (!drv->init || !(ret = drv->init())) 252 env_set_inited(drv->location); 253 254 debug("%s: Environment %s init done (ret=%d)\n", __func__, 255 drv->name, ret); 256 } 257 258 if (!prio) 259 return -ENODEV; 260 261 if (ret == -ENOENT) { 262 gd->env_addr = (ulong)&default_environment[0]; 263 gd->env_valid = ENV_VALID; 264 265 return 0; 266 } 267 268 return ret; 269 } 270