env.c 6.06 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0+
2 3 4 5 6 7 8 9 10 11
/*
 * Copyright (C) 2017 Google, Inc
 * Written by Simon Glass <sjg@chromium.org>
 */

#include <common.h>
#include <environment.h>

DECLARE_GLOBAL_DATA_PTR;

12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
void env_fix_drivers(void)
{
	struct env_driver *drv;
	const int n_ents = ll_entry_count(struct env_driver, env_driver);
	struct env_driver *entry;

	drv = ll_entry_start(struct env_driver, env_driver);
	for (entry = drv; entry != drv + n_ents; entry++) {
		if (entry->name)
			entry->name += gd->reloc_off;
		if (entry->load)
			entry->load += gd->reloc_off;
		if (entry->save)
			entry->save += gd->reloc_off;
		if (entry->init)
			entry->init += gd->reloc_off;
	}
}
#endif

33
static struct env_driver *_env_driver_lookup(enum env_location loc)
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
{
	struct env_driver *drv;
	const int n_ents = ll_entry_count(struct env_driver, env_driver);
	struct env_driver *entry;

	drv = ll_entry_start(struct env_driver, env_driver);
	for (entry = drv; entry != drv + n_ents; entry++) {
		if (loc == entry->location)
			return entry;
	}

	/* Not found */
	return NULL;
}

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
static enum env_location env_locations[] = {
#ifdef CONFIG_ENV_IS_IN_EEPROM
	ENVL_EEPROM,
#endif
#ifdef CONFIG_ENV_IS_IN_EXT4
	ENVL_EXT4,
#endif
#ifdef CONFIG_ENV_IS_IN_FAT
	ENVL_FAT,
#endif
#ifdef CONFIG_ENV_IS_IN_FLASH
	ENVL_FLASH,
#endif
#ifdef CONFIG_ENV_IS_IN_MMC
	ENVL_MMC,
#endif
#ifdef CONFIG_ENV_IS_IN_NAND
	ENVL_NAND,
#endif
#ifdef CONFIG_ENV_IS_IN_NVRAM
	ENVL_NVRAM,
#endif
#ifdef CONFIG_ENV_IS_IN_REMOTE
	ENVL_REMOTE,
#endif
74 75 76
#ifdef CONFIG_ENV_IS_IN_SATA
	ENVL_ESATA,
#endif
77 78 79 80 81 82 83 84 85 86 87
#ifdef CONFIG_ENV_IS_IN_SPI_FLASH
	ENVL_SPI_FLASH,
#endif
#ifdef CONFIG_ENV_IS_IN_UBI
	ENVL_UBI,
#endif
#ifdef CONFIG_ENV_IS_NOWHERE
	ENVL_NOWHERE,
#endif
};

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
static bool env_has_inited(enum env_location location)
{
	return gd->env_has_init & BIT(location);
}

static void env_set_inited(enum env_location location)
{
	/*
	 * We're using a 32-bits bitmask stored in gd (env_has_init)
	 * using the above enum value as the bit index. We need to
	 * make sure that we're not overflowing it.
	 */
	BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);

	gd->env_has_init |= BIT(location);
}

105 106 107 108 109 110 111
/**
 * env_get_location() - Returns the best env location for a board
 * @op: operations performed on the environment
 * @prio: priority between the multiple environments, 0 being the
 *        highest priority
 *
 * This will return the preferred environment for the given priority.
112
 * This is overridable by boards if they need to.
113 114 115 116 117 118 119 120 121 122
 *
 * All implementations are free to use the operation, the priority and
 * any other data relevant to their choice, but must take into account
 * the fact that the lowest prority (0) is the most important location
 * in the system. The following locations should be returned by order
 * of descending priorities, from the highest to the lowest priority.
 *
 * Returns:
 * an enum env_location value on success, a negative error code otherwise
 */
123
__weak enum env_location env_get_location(enum env_operation op, int prio)
124
{
125 126 127 128
	if (prio >= ARRAY_SIZE(env_locations))
		return ENVL_UNKNOWN;

	gd->env_load_prio = prio;
129

130
	return env_locations[prio];
131 132
}

133 134 135 136 137 138 139 140 141 142 143 144 145 146

/**
 * env_driver_lookup() - Finds the most suited environment location
 * @op: operations performed on the environment
 * @prio: priority between the multiple environments, 0 being the
 *        highest priority
 *
 * This will try to find the available environment with the highest
 * priority in the system.
 *
 * Returns:
 * NULL on error, a pointer to a struct env_driver otherwise
 */
static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
147
{
148
	enum env_location loc = env_get_location(op, prio);
149 150
	struct env_driver *drv;

151 152 153
	if (loc == ENVL_UNKNOWN)
		return NULL;

154
	drv = _env_driver_lookup(loc);
155 156 157 158 159 160 161 162 163
	if (!drv) {
		debug("%s: No environment driver for location %d\n", __func__,
		      loc);
		return NULL;
	}

	return drv;
}

164
__weak int env_get_char_spec(int index)
165
{
166 167
	return *(uchar *)(gd->env_addr + index);
}
168

169 170
int env_get_char(int index)
{
171
	if (gd->env_valid == ENV_INVALID)
172
		return default_environment[index];
173 174
	else
		return env_get_char_spec(index);
175 176 177 178
}

int env_load(void)
{
179
	struct env_driver *drv;
180
	int best_prio = -1;
181
	int prio;
182

183 184 185 186 187 188
	for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
		int ret;

		if (!drv->load)
			continue;

189 190 191
		if (!env_has_inited(drv->location))
			continue;

192
		printf("Loading Environment from %s... ", drv->name);
193 194 195 196 197
		/*
		 * In error case, the error message must be printed during
		 * drv->load() in some underlying API, and it must be exactly
		 * one message.
		 */
198
		ret = drv->load();
199
		if (!ret) {
200
			printf("OK\n");
201
			return 0;
202 203 204 205 206 207
		} else if (ret == -ENOMSG) {
			/* Handle "bad CRC" case */
			if (best_prio == -1)
				best_prio = prio;
		} else {
			debug("Failed (%d)\n", ret);
208
		}
209 210
	}

211 212
	/*
	 * In case of invalid environment, we set the 'default' env location
213 214 215 216 217 218
	 * to the best choice, i.e.:
	 *   1. Environment location with bad CRC, if such location was found
	 *   2. Otherwise use the location with highest priority
	 *
	 * This way, next calls to env_save() will restore the environment
	 * at the right place.
219
	 */
220 221 222 223 224
	if (best_prio >= 0)
		debug("Selecting environment with bad CRC\n");
	else
		best_prio = 0;
	env_get_location(ENVOP_LOAD, best_prio);
225

226
	return -ENODEV;
227 228 229 230
}

int env_save(void)
{
231
	struct env_driver *drv;
232

233 234
	drv = env_driver_lookup(ENVOP_SAVE, gd->env_load_prio);
	if (drv) {
235 236 237
		int ret;

		if (!drv->save)
238
			return -ENODEV;
239

240
		if (!env_has_inited(drv->location))
241
			return -ENODEV;
242

243
		printf("Saving Environment to %s... ", drv->name);
244
		ret = drv->save();
245 246 247 248 249
		if (ret)
			printf("Failed (%d)\n", ret);
		else
			printf("OK\n");

250 251
		if (!ret)
			return 0;
252 253
	}

254
	return -ENODEV;
255 256
}

Simon Glass's avatar
Simon Glass committed
257
int env_init(void)
258
{
259
	struct env_driver *drv;
260
	int ret = -ENOENT;
261 262 263
	int prio;

	for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
264 265
		if (!drv->init || !(ret = drv->init()))
			env_set_inited(drv->location);
266

267
		debug("%s: Environment %s init done (ret=%d)\n", __func__,
268 269 270 271 272 273
		      drv->name, ret);
	}

	if (!prio)
		return -ENODEV;

274 275
	if (ret == -ENOENT) {
		gd->env_addr = (ulong)&default_environment[0];
276
		gd->env_valid = ENV_VALID;
277 278

		return 0;
279 280
	}

281
	return ret;
282
}