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