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