Home | History | Annotate | Download | only in env
      1 // SPDX-License-Identifier: GPL-2.0+
      2 /*
      3  * Copyright (C) 2017 Google, Inc
      4  * Written by Simon Glass <sjg (at) chromium.org>
      5  */
      6 
      7 #include <common.h>
      8 #include <environment.h>
      9 
     10 DECLARE_GLOBAL_DATA_PTR;
     11 
     12 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
     13 void env_fix_drivers(void)
     14 {
     15 	struct env_driver *drv;
     16 	const int n_ents = ll_entry_count(struct env_driver, env_driver);
     17 	struct env_driver *entry;
     18 
     19 	drv = ll_entry_start(struct env_driver, env_driver);
     20 	for (entry = drv; entry != drv + n_ents; entry++) {
     21 		if (entry->name)
     22 			entry->name += gd->reloc_off;
     23 		if (entry->load)
     24 			entry->load += gd->reloc_off;
     25 		if (entry->save)
     26 			entry->save += gd->reloc_off;
     27 		if (entry->init)
     28 			entry->init += gd->reloc_off;
     29 	}
     30 }
     31 #endif
     32 
     33 static struct env_driver *_env_driver_lookup(enum env_location loc)
     34 {
     35 	struct env_driver *drv;
     36 	const int n_ents = ll_entry_count(struct env_driver, env_driver);
     37 	struct env_driver *entry;
     38 
     39 	drv = ll_entry_start(struct env_driver, env_driver);
     40 	for (entry = drv; entry != drv + n_ents; entry++) {
     41 		if (loc == entry->location)
     42 			return entry;
     43 	}
     44 
     45 	/* Not found */
     46 	return NULL;
     47 }
     48 
     49 static enum env_location env_locations[] = {
     50 #ifdef CONFIG_ENV_IS_IN_EEPROM
     51 	ENVL_EEPROM,
     52 #endif
     53 #ifdef CONFIG_ENV_IS_IN_EXT4
     54 	ENVL_EXT4,
     55 #endif
     56 #ifdef CONFIG_ENV_IS_IN_FAT
     57 	ENVL_FAT,
     58 #endif
     59 #ifdef CONFIG_ENV_IS_IN_FLASH
     60 	ENVL_FLASH,
     61 #endif
     62 #ifdef CONFIG_ENV_IS_IN_MMC
     63 	ENVL_MMC,
     64 #endif
     65 #ifdef CONFIG_ENV_IS_IN_NAND
     66 	ENVL_NAND,
     67 #endif
     68 #ifdef CONFIG_ENV_IS_IN_NVRAM
     69 	ENVL_NVRAM,
     70 #endif
     71 #ifdef CONFIG_ENV_IS_IN_REMOTE
     72 	ENVL_REMOTE,
     73 #endif
     74 #ifdef CONFIG_ENV_IS_IN_SPI_FLASH
     75 	ENVL_SPI_FLASH,
     76 #endif
     77 #ifdef CONFIG_ENV_IS_IN_UBI
     78 	ENVL_UBI,
     79 #endif
     80 #ifdef CONFIG_ENV_IS_NOWHERE
     81 	ENVL_NOWHERE,
     82 #endif
     83 };
     84 
     85 static bool env_has_inited(enum env_location location)
     86 {
     87 	return gd->env_has_init & BIT(location);
     88 }
     89 
     90 static void env_set_inited(enum env_location location)
     91 {
     92 	/*
     93 	 * We're using a 32-bits bitmask stored in gd (env_has_init)
     94 	 * using the above enum value as the bit index. We need to
     95 	 * make sure that we're not overflowing it.
     96 	 */
     97 	BUILD_BUG_ON(ARRAY_SIZE(env_locations) > BITS_PER_LONG);
     98 
     99 	gd->env_has_init |= BIT(location);
    100 }
    101 
    102 /**
    103  * env_get_location() - Returns the best env location for a board
    104  * @op: operations performed on the environment
    105  * @prio: priority between the multiple environments, 0 being the
    106  *        highest priority
    107  *
    108  * This will return the preferred environment for the given priority.
    109  * This is overridable by boards if they need to.
    110  *
    111  * All implementations are free to use the operation, the priority and
    112  * any other data relevant to their choice, but must take into account
    113  * the fact that the lowest prority (0) is the most important location
    114  * in the system. The following locations should be returned by order
    115  * of descending priorities, from the highest to the lowest priority.
    116  *
    117  * Returns:
    118  * an enum env_location value on success, a negative error code otherwise
    119  */
    120 __weak enum env_location env_get_location(enum env_operation op, int prio)
    121 {
    122 	switch (op) {
    123 	case ENVOP_GET_CHAR:
    124 	case ENVOP_INIT:
    125 	case ENVOP_LOAD:
    126 		if (prio >= ARRAY_SIZE(env_locations))
    127 			return ENVL_UNKNOWN;
    128 
    129 		gd->env_load_location = env_locations[prio];
    130 		return gd->env_load_location;
    131 
    132 	case ENVOP_SAVE:
    133 		return gd->env_load_location;
    134 	}
    135 
    136 	return ENVL_UNKNOWN;
    137 }
    138 
    139 
    140 /**
    141  * env_driver_lookup() - Finds the most suited environment location
    142  * @op: operations performed on the environment
    143  * @prio: priority between the multiple environments, 0 being the
    144  *        highest priority
    145  *
    146  * This will try to find the available environment with the highest
    147  * priority in the system.
    148  *
    149  * Returns:
    150  * NULL on error, a pointer to a struct env_driver otherwise
    151  */
    152 static struct env_driver *env_driver_lookup(enum env_operation op, int prio)
    153 {
    154 	enum env_location loc = env_get_location(op, prio);
    155 	struct env_driver *drv;
    156 
    157 	if (loc == ENVL_UNKNOWN)
    158 		return NULL;
    159 
    160 	drv = _env_driver_lookup(loc);
    161 	if (!drv) {
    162 		debug("%s: No environment driver for location %d\n", __func__,
    163 		      loc);
    164 		return NULL;
    165 	}
    166 
    167 	return drv;
    168 }
    169 
    170 __weak int env_get_char_spec(int index)
    171 {
    172 	return *(uchar *)(gd->env_addr + index);
    173 }
    174 
    175 int env_get_char(int index)
    176 {
    177 	if (gd->env_valid == ENV_INVALID)
    178 		return default_environment[index];
    179 	else
    180 		return env_get_char_spec(index);
    181 }
    182 
    183 int env_load(void)
    184 {
    185 	struct env_driver *drv;
    186 	int prio;
    187 
    188 	for (prio = 0; (drv = env_driver_lookup(ENVOP_LOAD, prio)); prio++) {
    189 		int ret;
    190 
    191 		if (!drv->load)
    192 			continue;
    193 
    194 		if (!env_has_inited(drv->location))
    195 			continue;
    196 
    197 		printf("Loading Environment from %s... ", drv->name);
    198 		ret = drv->load();
    199 		if (ret)
    200 			printf("Failed (%d)\n", ret);
    201 		else
    202 			printf("OK\n");
    203 
    204 		if (!ret)
    205 			return 0;
    206 	}
    207 
    208 	return -ENODEV;
    209 }
    210 
    211 int env_save(void)
    212 {
    213 	struct env_driver *drv;
    214 	int prio;
    215 
    216 	for (prio = 0; (drv = env_driver_lookup(ENVOP_SAVE, prio)); prio++) {
    217 		int ret;
    218 
    219 		if (!drv->save)
    220 			continue;
    221 
    222 		if (!env_has_inited(drv->location))
    223 			continue;
    224 
    225 		printf("Saving Environment to %s... ", drv->name);
    226 		ret = drv->save();
    227 		if (ret)
    228 			printf("Failed (%d)\n", ret);
    229 		else
    230 			printf("OK\n");
    231 
    232 		if (!ret)
    233 			return 0;
    234 	}
    235 
    236 	return -ENODEV;
    237 }
    238 
    239 int env_init(void)
    240 {
    241 	struct env_driver *drv;
    242 	int ret = -ENOENT;
    243 	int prio;
    244 
    245 	for (prio = 0; (drv = env_driver_lookup(ENVOP_INIT, prio)); prio++) {
    246 		if (!drv->init || !(ret = drv->init()))
    247 			env_set_inited(drv->location);
    248 
    249 		debug("%s: Environment %s init done (ret=%d)\n", __func__,
    250 		      drv->name, ret);
    251 	}
    252 
    253 	if (!prio)
    254 		return -ENODEV;
    255 
    256 	if (ret == -ENOENT) {
    257 		gd->env_addr = (ulong)&default_environment[0];
    258 		gd->env_valid = ENV_VALID;
    259 
    260 		return 0;
    261 	}
    262 
    263 	return ret;
    264 }
    265