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