xref: /openbmc/u-boot/env/env.c (revision 748ad078)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2c9d728ddSSimon Glass /*
3c9d728ddSSimon Glass  * Copyright (C) 2017 Google, Inc
4c9d728ddSSimon Glass  * Written by Simon Glass <sjg@chromium.org>
5c9d728ddSSimon Glass  */
6c9d728ddSSimon Glass 
7c9d728ddSSimon Glass #include <common.h>
8c9d728ddSSimon Glass #include <environment.h>
9c9d728ddSSimon Glass 
10c9d728ddSSimon Glass DECLARE_GLOBAL_DATA_PTR;
11c9d728ddSSimon Glass 
127bcdf195SSiva Durga Prasad Paladugu #if defined(CONFIG_NEEDS_MANUAL_RELOC)
env_fix_drivers(void)137bcdf195SSiva Durga Prasad Paladugu void env_fix_drivers(void)
147bcdf195SSiva Durga Prasad Paladugu {
157bcdf195SSiva Durga Prasad Paladugu 	struct env_driver *drv;
167bcdf195SSiva Durga Prasad Paladugu 	const int n_ents = ll_entry_count(struct env_driver, env_driver);
177bcdf195SSiva Durga Prasad Paladugu 	struct env_driver *entry;
187bcdf195SSiva Durga Prasad Paladugu 
197bcdf195SSiva Durga Prasad Paladugu 	drv = ll_entry_start(struct env_driver, env_driver);
207bcdf195SSiva Durga Prasad Paladugu 	for (entry = drv; entry != drv + n_ents; entry++) {
217bcdf195SSiva Durga Prasad Paladugu 		if (entry->name)
227bcdf195SSiva Durga Prasad Paladugu 			entry->name += gd->reloc_off;
237bcdf195SSiva Durga Prasad Paladugu 		if (entry->load)
247bcdf195SSiva Durga Prasad Paladugu 			entry->load += gd->reloc_off;
257bcdf195SSiva Durga Prasad Paladugu 		if (entry->save)
267bcdf195SSiva Durga Prasad Paladugu 			entry->save += gd->reloc_off;
277bcdf195SSiva Durga Prasad Paladugu 		if (entry->init)
287bcdf195SSiva Durga Prasad Paladugu 			entry->init += gd->reloc_off;
297bcdf195SSiva Durga Prasad Paladugu 	}
307bcdf195SSiva Durga Prasad Paladugu }
317bcdf195SSiva Durga Prasad Paladugu #endif
327bcdf195SSiva Durga Prasad Paladugu 
_env_driver_lookup(enum env_location loc)3352746c43SMaxime Ripard static struct env_driver *_env_driver_lookup(enum env_location loc)
34c9d728ddSSimon Glass {
35c9d728ddSSimon Glass 	struct env_driver *drv;
36c9d728ddSSimon Glass 	const int n_ents = ll_entry_count(struct env_driver, env_driver);
37c9d728ddSSimon Glass 	struct env_driver *entry;
38c9d728ddSSimon Glass 
39c9d728ddSSimon Glass 	drv = ll_entry_start(struct env_driver, env_driver);
40c9d728ddSSimon Glass 	for (entry = drv; entry != drv + n_ents; entry++) {
41c9d728ddSSimon Glass 		if (loc == entry->location)
42c9d728ddSSimon Glass 			return entry;
43c9d728ddSSimon Glass 	}
44c9d728ddSSimon Glass 
45c9d728ddSSimon Glass 	/* Not found */
46c9d728ddSSimon Glass 	return NULL;
47c9d728ddSSimon Glass }
48c9d728ddSSimon Glass 
497d714a24SMaxime Ripard static enum env_location env_locations[] = {
507d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_EEPROM
517d714a24SMaxime Ripard 	ENVL_EEPROM,
527d714a24SMaxime Ripard #endif
537d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_EXT4
547d714a24SMaxime Ripard 	ENVL_EXT4,
557d714a24SMaxime Ripard #endif
567d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_FAT
577d714a24SMaxime Ripard 	ENVL_FAT,
587d714a24SMaxime Ripard #endif
597d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_FLASH
607d714a24SMaxime Ripard 	ENVL_FLASH,
617d714a24SMaxime Ripard #endif
627d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_MMC
637d714a24SMaxime Ripard 	ENVL_MMC,
647d714a24SMaxime Ripard #endif
657d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_NAND
667d714a24SMaxime Ripard 	ENVL_NAND,
677d714a24SMaxime Ripard #endif
687d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_NVRAM
697d714a24SMaxime Ripard 	ENVL_NVRAM,
707d714a24SMaxime Ripard #endif
717d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_REMOTE
727d714a24SMaxime Ripard 	ENVL_REMOTE,
737d714a24SMaxime Ripard #endif
749a6a311dSYe Li #ifdef CONFIG_ENV_IS_IN_SATA
759a6a311dSYe Li 	ENVL_ESATA,
769a6a311dSYe Li #endif
777d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
787d714a24SMaxime Ripard 	ENVL_SPI_FLASH,
797d714a24SMaxime Ripard #endif
807d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_UBI
817d714a24SMaxime Ripard 	ENVL_UBI,
827d714a24SMaxime Ripard #endif
837d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_NOWHERE
847d714a24SMaxime Ripard 	ENVL_NOWHERE,
857d714a24SMaxime Ripard #endif
867d714a24SMaxime Ripard };
877d714a24SMaxime Ripard 
env_has_inited(enum env_location location)881d446087SMaxime Ripard static bool env_has_inited(enum env_location location)
891d446087SMaxime Ripard {
901d446087SMaxime Ripard 	return gd->env_has_init & BIT(location);
911d446087SMaxime Ripard }
921d446087SMaxime Ripard 
env_set_inited(enum env_location location)931d446087SMaxime Ripard static void env_set_inited(enum env_location location)
941d446087SMaxime Ripard {
951d446087SMaxime Ripard 	/*
961d446087SMaxime Ripard 	 * We're using a 32-bits bitmask stored in gd (env_has_init)
971d446087SMaxime Ripard 	 * using the above enum value as the bit index. We need to
981d446087SMaxime Ripard 	 * make sure that we're not overflowing it.
991d446087SMaxime Ripard 	 */
1001d446087SMaxime Ripard 	BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
1011d446087SMaxime Ripard 
1021d446087SMaxime Ripard 	gd->env_has_init |= BIT(location);
1031d446087SMaxime Ripard }
1041d446087SMaxime Ripard 
1058a3a7e22SMaxime Ripard /**
1068a3a7e22SMaxime Ripard  * env_get_location() - Returns the best env location for a board
1078a3a7e22SMaxime Ripard  * @op: operations performed on the environment
1088a3a7e22SMaxime Ripard  * @prio: priority between the multiple environments, 0 being the
1098a3a7e22SMaxime Ripard  *        highest priority
1108a3a7e22SMaxime Ripard  *
1118a3a7e22SMaxime Ripard  * This will return the preferred environment for the given priority.
11240c08a68SMaxime Ripard  * This is overridable by boards if they need to.
1138a3a7e22SMaxime Ripard  *
1148a3a7e22SMaxime Ripard  * All implementations are free to use the operation, the priority and
1158a3a7e22SMaxime Ripard  * any other data relevant to their choice, but must take into account
1168a3a7e22SMaxime Ripard  * the fact that the lowest prority (0) is the most important location
1178a3a7e22SMaxime Ripard  * in the system. The following locations should be returned by order
1188a3a7e22SMaxime Ripard  * of descending priorities, from the highest to the lowest priority.
1198a3a7e22SMaxime Ripard  *
1208a3a7e22SMaxime Ripard  * Returns:
1218a3a7e22SMaxime Ripard  * an enum env_location value on success, a negative error code otherwise
1228a3a7e22SMaxime Ripard  */
env_get_location(enum env_operation op,int prio)12340c08a68SMaxime Ripard __weak enum env_location env_get_location(enum env_operation op, int prio)
124c9d728ddSSimon Glass {
1257d714a24SMaxime Ripard 	if (prio >= ARRAY_SIZE(env_locations))
1268a3a7e22SMaxime Ripard 		return ENVL_UNKNOWN;
1278a3a7e22SMaxime Ripard 
128d30ba231SNicholas Faustini 	gd->env_load_prio = prio;
1297d714a24SMaxime Ripard 
130d30ba231SNicholas Faustini 	return env_locations[prio];
131c9d728ddSSimon Glass }
132c9d728ddSSimon Glass 
1338a3a7e22SMaxime Ripard 
1348a3a7e22SMaxime Ripard /**
1358a3a7e22SMaxime Ripard  * env_driver_lookup() - Finds the most suited environment location
1368a3a7e22SMaxime Ripard  * @op: operations performed on the environment
1378a3a7e22SMaxime Ripard  * @prio: priority between the multiple environments, 0 being the
1388a3a7e22SMaxime Ripard  *        highest priority
1398a3a7e22SMaxime Ripard  *
1408a3a7e22SMaxime Ripard  * This will try to find the available environment with the highest
1418a3a7e22SMaxime Ripard  * priority in the system.
1428a3a7e22SMaxime Ripard  *
1438a3a7e22SMaxime Ripard  * Returns:
1448a3a7e22SMaxime Ripard  * NULL on error, a pointer to a struct env_driver otherwise
1458a3a7e22SMaxime Ripard  */
env_driver_lookup(enum env_operation op,int prio)1468a3a7e22SMaxime Ripard static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
147c9d728ddSSimon Glass {
1488a3a7e22SMaxime Ripard 	enum env_location loc = env_get_location(op, prio);
149c9d728ddSSimon Glass 	struct env_driver *drv;
150c9d728ddSSimon Glass 
1518a3a7e22SMaxime Ripard 	if (loc == ENVL_UNKNOWN)
1528a3a7e22SMaxime Ripard 		return NULL;
1538a3a7e22SMaxime Ripard 
15452746c43SMaxime Ripard 	drv = _env_driver_lookup(loc);
155c9d728ddSSimon Glass 	if (!drv) {
156c9d728ddSSimon Glass 		debug("%s: No environment driver for location %d\n", __func__,
157c9d728ddSSimon Glass 		      loc);
158c9d728ddSSimon Glass 		return NULL;
159c9d728ddSSimon Glass 	}
160c9d728ddSSimon Glass 
161c9d728ddSSimon Glass 	return drv;
162c9d728ddSSimon Glass }
163c9d728ddSSimon Glass 
env_get_char_spec(int index)164b2cdef48SGoldschmidt Simon __weak int env_get_char_spec(int index)
165c9d728ddSSimon Glass {
166b2cdef48SGoldschmidt Simon 	return *(uchar *)(gd->env_addr + index);
167c9d728ddSSimon Glass }
168c9d728ddSSimon Glass 
env_get_char(int index)169b2cdef48SGoldschmidt Simon int env_get_char(int index)
170b2cdef48SGoldschmidt Simon {
171b2cdef48SGoldschmidt Simon 	if (gd->env_valid == ENV_INVALID)
172b2cdef48SGoldschmidt Simon 		return default_environment[index];
173b2cdef48SGoldschmidt Simon 	else
174b2cdef48SGoldschmidt Simon 		return env_get_char_spec(index);
175c9d728ddSSimon Glass }
176c9d728ddSSimon Glass 
env_load(void)177c9d728ddSSimon Glass int env_load(void)
178c9d728ddSSimon Glass {
1798a3a7e22SMaxime Ripard 	struct env_driver *drv;
180*293a172bSSam Protsenko 	int best_prio = -1;
1818a3a7e22SMaxime Ripard 	int prio;
182c9d728ddSSimon Glass 
1838a3a7e22SMaxime Ripard 	for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
1848a3a7e22SMaxime Ripard 		int ret;
1858a3a7e22SMaxime Ripard 
186c9d728ddSSimon Glass 		if (!drv->load)
1878a3a7e22SMaxime Ripard 			continue;
1888a3a7e22SMaxime Ripard 
1891d446087SMaxime Ripard 		if (!env_has_inited(drv->location))
1901d446087SMaxime Ripard 			continue;
1911d446087SMaxime Ripard 
1923574ba01SMaxime Ripard 		printf("Loading Environment from %s... ", drv->name);
19313bbfb4aSSam Protsenko 		/*
19413bbfb4aSSam Protsenko 		 * In error case, the error message must be printed during
19513bbfb4aSSam Protsenko 		 * drv->load() in some underlying API, and it must be exactly
19613bbfb4aSSam Protsenko 		 * one message.
19713bbfb4aSSam Protsenko 		 */
198c55d8b94SSimon Glass 		ret = drv->load();
199*293a172bSSam Protsenko 		if (!ret) {
2003574ba01SMaxime Ripard 			printf("OK\n");
2018a3a7e22SMaxime Ripard 			return 0;
202*293a172bSSam Protsenko 		} else if (ret == -ENOMSG) {
203*293a172bSSam Protsenko 			/* Handle "bad CRC" case */
204*293a172bSSam Protsenko 			if (best_prio == -1)
205*293a172bSSam Protsenko 				best_prio = prio;
206*293a172bSSam Protsenko 		} else {
207*293a172bSSam Protsenko 			debug("Failed (%d)\n", ret);
208c9d728ddSSimon Glass 		}
20913bbfb4aSSam Protsenko 	}
210c9d728ddSSimon Glass 
211d30ba231SNicholas Faustini 	/*
212d30ba231SNicholas Faustini 	 * In case of invalid environment, we set the 'default' env location
213*293a172bSSam Protsenko 	 * to the best choice, i.e.:
214*293a172bSSam Protsenko 	 *   1. Environment location with bad CRC, if such location was found
215*293a172bSSam Protsenko 	 *   2. Otherwise use the location with highest priority
216*293a172bSSam Protsenko 	 *
217*293a172bSSam Protsenko 	 * This way, next calls to env_save() will restore the environment
218*293a172bSSam Protsenko 	 * at the right place.
219d30ba231SNicholas Faustini 	 */
220*293a172bSSam Protsenko 	if (best_prio >= 0)
221*293a172bSSam Protsenko 		debug("Selecting environment with bad CRC\n");
222*293a172bSSam Protsenko 	else
223*293a172bSSam Protsenko 		best_prio = 0;
224*293a172bSSam Protsenko 	env_get_location(ENVOP_LOAD, best_prio);
225d30ba231SNicholas Faustini 
2268a3a7e22SMaxime Ripard 	return -ENODEV;
227c9d728ddSSimon Glass }
228c9d728ddSSimon Glass 
env_save(void)229c9d728ddSSimon Glass int env_save(void)
230c9d728ddSSimon Glass {
2318a3a7e22SMaxime Ripard 	struct env_driver *drv;
2328a3a7e22SMaxime Ripard 
233d30ba231SNicholas Faustini 	drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
234d30ba231SNicholas Faustini 	if (drv) {
235c9d728ddSSimon Glass 		int ret;
236c9d728ddSSimon Glass 
237c9d728ddSSimon Glass 		if (!drv->save)
238d30ba231SNicholas Faustini 			return -ENODEV;
2399c24dfb2SMaxime Ripard 
2401d446087SMaxime Ripard 		if (!env_has_inited(drv->location))
241d30ba231SNicholas Faustini 			return -ENODEV;
2421d446087SMaxime Ripard 
2439efac3c8SMaxime Ripard 		printf("Saving Environment to %s... ", drv->name);
244c9d728ddSSimon Glass 		ret = drv->save();
2453574ba01SMaxime Ripard 		if (ret)
2463574ba01SMaxime Ripard 			printf("Failed (%d)\n", ret);
2473574ba01SMaxime Ripard 		else
2483574ba01SMaxime Ripard 			printf("OK\n");
2493574ba01SMaxime Ripard 
2508a3a7e22SMaxime Ripard 		if (!ret)
2518a3a7e22SMaxime Ripard 			return 0;
252c9d728ddSSimon Glass 	}
253c9d728ddSSimon Glass 
2548a3a7e22SMaxime Ripard 	return -ENODEV;
255c9d728ddSSimon Glass }
256c9d728ddSSimon Glass 
env_init(void)2576eeae424SSimon Glass int env_init(void)
258c9d728ddSSimon Glass {
2598a3a7e22SMaxime Ripard 	struct env_driver *drv;
2607938822aSSimon Glass 	int ret = -ENOENT;
2618a3a7e22SMaxime Ripard 	int prio;
262c9d728ddSSimon Glass 
2638a3a7e22SMaxime Ripard 	for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
2641d446087SMaxime Ripard 		if (!drv->init || !(ret = drv->init()))
2651d446087SMaxime Ripard 			env_set_inited(drv->location);
2668a3a7e22SMaxime Ripard 
2671d446087SMaxime Ripard 		debug("%s: Environment %s init done (ret=%d)\n", __func__,
2688a3a7e22SMaxime Ripard 		      drv->name, ret);
2698a3a7e22SMaxime Ripard 	}
2708a3a7e22SMaxime Ripard 
2718a3a7e22SMaxime Ripard 	if (!prio)
2728a3a7e22SMaxime Ripard 		return -ENODEV;
2738a3a7e22SMaxime Ripard 
2747938822aSSimon Glass 	if (ret == -ENOENT) {
2757938822aSSimon Glass 		gd->env_addr = (ulong)&default_environment[0];
276eeba55cbSTom Rini 		gd->env_valid = ENV_VALID;
2777938822aSSimon Glass 
2787938822aSSimon Glass 		return 0;
279c9d728ddSSimon Glass 	}
280c9d728ddSSimon Glass 
2818a3a7e22SMaxime Ripard 	return ret;
282c9d728ddSSimon Glass }
283