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