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_SPI_FLASH 75 ENVL_SPI_FLASH, 76 #endif 77 #ifdef CONFIG_ENV_IS_IN_UBI 78 ENVL_UBI, 79 #endif 80 #ifdef CONFIG_ENV_IS_NOWHERE 81 ENVL_NOWHERE, 82 #endif 83 }; 84 85 static bool env_has_inited(enum env_location location) 86 { 87 return gd->env_has_init & BIT(location); 88 } 89 90 static void env_set_inited(enum env_location location) 91 { 92 /* 93 * We're using a 32-bits bitmask stored in gd (env_has_init) 94 * using the above enum value as the bit index. We need to 95 * make sure that we're not overflowing it. 96 */ 97 BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG); 98 99 gd->env_has_init |= BIT(location); 100 } 101 102 /** 103 * env_get_location() - Returns the best env location for a board 104 * @op: operations performed on the environment 105 * @prio: priority between the multiple environments, 0 being the 106 * highest priority 107 * 108 * This will return the preferred environment for the given priority. 109 * This is overridable by boards if they need to. 110 * 111 * All implementations are free to use the operation, the priority and 112 * any other data relevant to their choice, but must take into account 113 * the fact that the lowest prority (0) is the most important location 114 * in the system. The following locations should be returned by order 115 * of descending priorities, from the highest to the lowest priority. 116 * 117 * Returns: 118 * an enum env_location value on success, a negative error code otherwise 119 */ 120 __weak enum env_location env_get_location(enum env_operation op, int prio) 121 { 122 if (prio >= ARRAY_SIZE(env_locations)) 123 return ENVL_UNKNOWN; 124 125 gd->env_load_prio = prio; 126 127 return env_locations[prio]; 128 } 129 130 131 /** 132 * env_driver_lookup() - Finds the most suited environment location 133 * @op: operations performed on the environment 134 * @prio: priority between the multiple environments, 0 being the 135 * highest priority 136 * 137 * This will try to find the available environment with the highest 138 * priority in the system. 139 * 140 * Returns: 141 * NULL on error, a pointer to a struct env_driver otherwise 142 */ 143 static struct env_driver *env_driver_lookup(enum env_operation op, int prio) 144 { 145 enum env_location loc = env_get_location(op, prio); 146 struct env_driver *drv; 147 148 if (loc == ENVL_UNKNOWN) 149 return NULL; 150 151 drv = _env_driver_lookup(loc); 152 if (!drv) { 153 debug("%s: No environment driver for location %d\n", __func__, 154 loc); 155 return NULL; 156 } 157 158 return drv; 159 } 160 161 __weak int env_get_char_spec(int index) 162 { 163 return *(uchar *)(gd->env_addr + index); 164 } 165 166 int env_get_char(int index) 167 { 168 if (gd->env_valid == ENV_INVALID) 169 return default_environment[index]; 170 else 171 return env_get_char_spec(index); 172 } 173 174 int env_load(void) 175 { 176 struct env_driver *drv; 177 int prio; 178 179 for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) { 180 int ret; 181 182 if (!drv->load) 183 continue; 184 185 if (!env_has_inited(drv->location)) 186 continue; 187 188 printf("Loading Environment from %s... ", drv->name); 189 ret = drv->load(); 190 if (ret) 191 printf("Failed (%d)\n", ret); 192 else 193 printf("OK\n"); 194 195 if (!ret) 196 return 0; 197 } 198 199 /* 200 * In case of invalid environment, we set the 'default' env location 201 * to the highest priority. In this way, next calls to env_save() 202 * will restore the environment at the right place. 203 */ 204 env_get_location(ENVOP_LOAD, 0); 205 206 return -ENODEV; 207 } 208 209 int env_save(void) 210 { 211 struct env_driver *drv; 212 213 drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio); 214 if (drv) { 215 int ret; 216 217 if (!drv->save) 218 return -ENODEV; 219 220 if (!env_has_inited(drv->location)) 221 return -ENODEV; 222 223 printf("Saving Environment to %s... ", drv->name); 224 ret = drv->save(); 225 if (ret) 226 printf("Failed (%d)\n", ret); 227 else 228 printf("OK\n"); 229 230 if (!ret) 231 return 0; 232 } 233 234 return -ENODEV; 235 } 236 237 int env_init(void) 238 { 239 struct env_driver *drv; 240 int ret = -ENOENT; 241 int prio; 242 243 for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) { 244 if (!drv->init || !(ret = drv->init())) 245 env_set_inited(drv->location); 246 247 debug("%s: Environment %s init done (ret=%d)\n", __func__, 248 drv->name, ret); 249 } 250 251 if (!prio) 252 return -ENODEV; 253 254 if (ret == -ENOENT) { 255 gd->env_addr = (ulong)&default_environment[0]; 256 gd->env_valid = ENV_VALID; 257 258 return 0; 259 } 260 261 return ret; 262 } 263