xref: /openbmc/u-boot/env/env.c (revision 13bbfb4a)
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)
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 
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
747d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
757d714a24SMaxime Ripard 	ENVL_SPI_FLASH,
767d714a24SMaxime Ripard #endif
777d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_IN_UBI
787d714a24SMaxime Ripard 	ENVL_UBI,
797d714a24SMaxime Ripard #endif
807d714a24SMaxime Ripard #ifdef CONFIG_ENV_IS_NOWHERE
817d714a24SMaxime Ripard 	ENVL_NOWHERE,
827d714a24SMaxime Ripard #endif
837d714a24SMaxime Ripard };
847d714a24SMaxime Ripard 
851d446087SMaxime Ripard static bool env_has_inited(enum env_location location)
861d446087SMaxime Ripard {
871d446087SMaxime Ripard 	return gd->env_has_init & BIT(location);
881d446087SMaxime Ripard }
891d446087SMaxime Ripard 
901d446087SMaxime Ripard static void env_set_inited(enum env_location location)
911d446087SMaxime Ripard {
921d446087SMaxime Ripard 	/*
931d446087SMaxime Ripard 	 * We're using a 32-bits bitmask stored in gd (env_has_init)
941d446087SMaxime Ripard 	 * using the above enum value as the bit index. We need to
951d446087SMaxime Ripard 	 * make sure that we're not overflowing it.
961d446087SMaxime Ripard 	 */
971d446087SMaxime Ripard 	BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
981d446087SMaxime Ripard 
991d446087SMaxime Ripard 	gd->env_has_init |= BIT(location);
1001d446087SMaxime Ripard }
1011d446087SMaxime Ripard 
1028a3a7e22SMaxime Ripard /**
1038a3a7e22SMaxime Ripard  * env_get_location() - Returns the best env location for a board
1048a3a7e22SMaxime Ripard  * @op: operations performed on the environment
1058a3a7e22SMaxime Ripard  * @prio: priority between the multiple environments, 0 being the
1068a3a7e22SMaxime Ripard  *        highest priority
1078a3a7e22SMaxime Ripard  *
1088a3a7e22SMaxime Ripard  * This will return the preferred environment for the given priority.
10940c08a68SMaxime Ripard  * This is overridable by boards if they need to.
1108a3a7e22SMaxime Ripard  *
1118a3a7e22SMaxime Ripard  * All implementations are free to use the operation, the priority and
1128a3a7e22SMaxime Ripard  * any other data relevant to their choice, but must take into account
1138a3a7e22SMaxime Ripard  * the fact that the lowest prority (0) is the most important location
1148a3a7e22SMaxime Ripard  * in the system. The following locations should be returned by order
1158a3a7e22SMaxime Ripard  * of descending priorities, from the highest to the lowest priority.
1168a3a7e22SMaxime Ripard  *
1178a3a7e22SMaxime Ripard  * Returns:
1188a3a7e22SMaxime Ripard  * an enum env_location value on success, a negative error code otherwise
1198a3a7e22SMaxime Ripard  */
12040c08a68SMaxime Ripard __weak enum env_location env_get_location(enum env_operation op, int prio)
121c9d728ddSSimon Glass {
1227d714a24SMaxime Ripard 	if (prio >= ARRAY_SIZE(env_locations))
1238a3a7e22SMaxime Ripard 		return ENVL_UNKNOWN;
1248a3a7e22SMaxime Ripard 
125d30ba231SNicholas Faustini 	gd->env_load_prio = prio;
1267d714a24SMaxime Ripard 
127d30ba231SNicholas Faustini 	return env_locations[prio];
128c9d728ddSSimon Glass }
129c9d728ddSSimon Glass 
1308a3a7e22SMaxime Ripard 
1318a3a7e22SMaxime Ripard /**
1328a3a7e22SMaxime Ripard  * env_driver_lookup() - Finds the most suited environment location
1338a3a7e22SMaxime Ripard  * @op: operations performed on the environment
1348a3a7e22SMaxime Ripard  * @prio: priority between the multiple environments, 0 being the
1358a3a7e22SMaxime Ripard  *        highest priority
1368a3a7e22SMaxime Ripard  *
1378a3a7e22SMaxime Ripard  * This will try to find the available environment with the highest
1388a3a7e22SMaxime Ripard  * priority in the system.
1398a3a7e22SMaxime Ripard  *
1408a3a7e22SMaxime Ripard  * Returns:
1418a3a7e22SMaxime Ripard  * NULL on error, a pointer to a struct env_driver otherwise
1428a3a7e22SMaxime Ripard  */
1438a3a7e22SMaxime Ripard static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
144c9d728ddSSimon Glass {
1458a3a7e22SMaxime Ripard 	enum env_location loc = env_get_location(op, prio);
146c9d728ddSSimon Glass 	struct env_driver *drv;
147c9d728ddSSimon Glass 
1488a3a7e22SMaxime Ripard 	if (loc == ENVL_UNKNOWN)
1498a3a7e22SMaxime Ripard 		return NULL;
1508a3a7e22SMaxime Ripard 
15152746c43SMaxime Ripard 	drv = _env_driver_lookup(loc);
152c9d728ddSSimon Glass 	if (!drv) {
153c9d728ddSSimon Glass 		debug("%s: No environment driver for location %d\n", __func__,
154c9d728ddSSimon Glass 		      loc);
155c9d728ddSSimon Glass 		return NULL;
156c9d728ddSSimon Glass 	}
157c9d728ddSSimon Glass 
158c9d728ddSSimon Glass 	return drv;
159c9d728ddSSimon Glass }
160c9d728ddSSimon Glass 
161b2cdef48SGoldschmidt Simon __weak int env_get_char_spec(int index)
162c9d728ddSSimon Glass {
163b2cdef48SGoldschmidt Simon 	return *(uchar *)(gd->env_addr + index);
164c9d728ddSSimon Glass }
165c9d728ddSSimon Glass 
166b2cdef48SGoldschmidt Simon int env_get_char(int index)
167b2cdef48SGoldschmidt Simon {
168b2cdef48SGoldschmidt Simon 	if (gd->env_valid == ENV_INVALID)
169b2cdef48SGoldschmidt Simon 		return default_environment[index];
170b2cdef48SGoldschmidt Simon 	else
171b2cdef48SGoldschmidt Simon 		return env_get_char_spec(index);
172c9d728ddSSimon Glass }
173c9d728ddSSimon Glass 
174c9d728ddSSimon Glass int env_load(void)
175c9d728ddSSimon Glass {
1768a3a7e22SMaxime Ripard 	struct env_driver *drv;
1778a3a7e22SMaxime Ripard 	int prio;
178c9d728ddSSimon Glass 
1798a3a7e22SMaxime Ripard 	for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
1808a3a7e22SMaxime Ripard 		int ret;
1818a3a7e22SMaxime Ripard 
182c9d728ddSSimon Glass 		if (!drv->load)
1838a3a7e22SMaxime Ripard 			continue;
1848a3a7e22SMaxime Ripard 
1851d446087SMaxime Ripard 		if (!env_has_inited(drv->location))
1861d446087SMaxime Ripard 			continue;
1871d446087SMaxime Ripard 
1883574ba01SMaxime Ripard 		printf("Loading Environment from %s... ", drv->name);
189*13bbfb4aSSam Protsenko 		/*
190*13bbfb4aSSam Protsenko 		 * In error case, the error message must be printed during
191*13bbfb4aSSam Protsenko 		 * drv->load() in some underlying API, and it must be exactly
192*13bbfb4aSSam Protsenko 		 * one message.
193*13bbfb4aSSam Protsenko 		 */
194c55d8b94SSimon Glass 		ret = drv->load();
195*13bbfb4aSSam Protsenko 		if (ret) {
196*13bbfb4aSSam Protsenko 			debug("Failed (%d)\n", ret);
197*13bbfb4aSSam Protsenko 		} else {
1983574ba01SMaxime Ripard 			printf("OK\n");
1998a3a7e22SMaxime Ripard 			return 0;
200c9d728ddSSimon Glass 		}
201*13bbfb4aSSam Protsenko 	}
202c9d728ddSSimon Glass 
203d30ba231SNicholas Faustini 	/*
204d30ba231SNicholas Faustini 	 * In case of invalid environment, we set the 'default' env location
205d30ba231SNicholas Faustini 	 * to the highest priority. In this way, next calls to env_save()
206d30ba231SNicholas Faustini 	 * will restore the environment at the right place.
207d30ba231SNicholas Faustini 	 */
208d30ba231SNicholas Faustini 	env_get_location(ENVOP_LOAD, 0);
209d30ba231SNicholas Faustini 
2108a3a7e22SMaxime Ripard 	return -ENODEV;
211c9d728ddSSimon Glass }
212c9d728ddSSimon Glass 
213c9d728ddSSimon Glass int env_save(void)
214c9d728ddSSimon Glass {
2158a3a7e22SMaxime Ripard 	struct env_driver *drv;
2168a3a7e22SMaxime Ripard 
217d30ba231SNicholas Faustini 	drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
218d30ba231SNicholas Faustini 	if (drv) {
219c9d728ddSSimon Glass 		int ret;
220c9d728ddSSimon Glass 
221c9d728ddSSimon Glass 		if (!drv->save)
222d30ba231SNicholas Faustini 			return -ENODEV;
2239c24dfb2SMaxime Ripard 
2241d446087SMaxime Ripard 		if (!env_has_inited(drv->location))
225d30ba231SNicholas Faustini 			return -ENODEV;
2261d446087SMaxime Ripard 
2279efac3c8SMaxime Ripard 		printf("Saving Environment to %s... ", drv->name);
228c9d728ddSSimon Glass 		ret = drv->save();
2293574ba01SMaxime Ripard 		if (ret)
2303574ba01SMaxime Ripard 			printf("Failed (%d)\n", ret);
2313574ba01SMaxime Ripard 		else
2323574ba01SMaxime Ripard 			printf("OK\n");
2333574ba01SMaxime Ripard 
2348a3a7e22SMaxime Ripard 		if (!ret)
2358a3a7e22SMaxime Ripard 			return 0;
236c9d728ddSSimon Glass 	}
237c9d728ddSSimon Glass 
2388a3a7e22SMaxime Ripard 	return -ENODEV;
239c9d728ddSSimon Glass }
240c9d728ddSSimon Glass 
2416eeae424SSimon Glass int env_init(void)
242c9d728ddSSimon Glass {
2438a3a7e22SMaxime Ripard 	struct env_driver *drv;
2447938822aSSimon Glass 	int ret = -ENOENT;
2458a3a7e22SMaxime Ripard 	int prio;
246c9d728ddSSimon Glass 
2478a3a7e22SMaxime Ripard 	for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
2481d446087SMaxime Ripard 		if (!drv->init || !(ret = drv->init()))
2491d446087SMaxime Ripard 			env_set_inited(drv->location);
2508a3a7e22SMaxime Ripard 
2511d446087SMaxime Ripard 		debug("%s: Environment %s init done (ret=%d)\n", __func__,
2528a3a7e22SMaxime Ripard 		      drv->name, ret);
2538a3a7e22SMaxime Ripard 	}
2548a3a7e22SMaxime Ripard 
2558a3a7e22SMaxime Ripard 	if (!prio)
2568a3a7e22SMaxime Ripard 		return -ENODEV;
2578a3a7e22SMaxime Ripard 
2587938822aSSimon Glass 	if (ret == -ENOENT) {
2597938822aSSimon Glass 		gd->env_addr = (ulong)&default_environment[0];
260eeba55cbSTom Rini 		gd->env_valid = ENV_VALID;
2617938822aSSimon Glass 
2627938822aSSimon Glass 		return 0;
263c9d728ddSSimon Glass 	}
264c9d728ddSSimon Glass 
2658a3a7e22SMaxime Ripard 	return ret;
266c9d728ddSSimon Glass }
267